Showing preview only (849K chars total). Download the full file or copy to clipboard to get everything.
Repository: pavlobu/deskreen
Branch: master
Commit: 31ea0b5e65c8
Files: 266
Total size: 774.9 KB
Directory structure:
gitextract_dgsxokkm/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── 1-Bug_report.md
│ │ └── 3-Feature_request.md
│ ├── config.yml
│ ├── stale.yml
│ └── workflows/
│ └── release.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── biome.json
├── build/
│ ├── entitlements.mac.plist
│ └── icon.icns
├── dev-app-update.yml
├── electron-builder.yml
├── electron.vite.config.ts
├── package.json
├── scripts/
│ ├── bump-version.js
│ └── undo-version-bump.js
├── src/
│ ├── client-viewer/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── public/
│ │ │ ├── img/
│ │ │ │ └── .gitkeep
│ │ │ ├── locales/
│ │ │ │ ├── da/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── de/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── en/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── es/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── fi/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── fr/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── it/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── ja/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── ko/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── nl/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── ru/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── sv/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── ua/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── zh_CN/
│ │ │ │ │ └── translation.json
│ │ │ │ └── zh_TW/
│ │ │ │ └── translation.json
│ │ │ ├── manifest.json
│ │ │ └── robots.txt
│ │ ├── scripts/
│ │ │ └── ga-interceptor.js
│ │ ├── src/
│ │ │ ├── App.css
│ │ │ ├── App.tsx
│ │ │ ├── api/
│ │ │ │ ├── config.ts
│ │ │ │ └── generator.ts
│ │ │ ├── assets/
│ │ │ │ ├── index.html
│ │ │ │ ├── locales/
│ │ │ │ │ ├── da/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── de/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── en/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── es/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── fi/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── fr/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── it/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── ja/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── ko/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── nl/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── ru/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── sv/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── ua/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── zh_CN/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ └── zh_TW/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── manifest.json
│ │ │ │ └── robots.txt
│ │ │ ├── components/
│ │ │ │ ├── ConnectingIndicator/
│ │ │ │ │ ├── ConnectingIndicatorIcon.tsx
│ │ │ │ │ ├── LoadingSharingIcon.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── ErrorDialog/
│ │ │ │ │ ├── ErrorMessageEnum.ts
│ │ │ │ │ ├── index.css
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── LoadingScreen/
│ │ │ │ │ ├── LoadingScreen.css
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── MyDeviceInfoCard/
│ │ │ │ │ ├── DeviceDetails.d.ts
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── PlayerControlPanel/
│ │ │ │ │ ├── handlePlayerToggleFullscreen.ts
│ │ │ │ │ ├── index.css
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── initScreenfullOnChange.ts
│ │ │ │ ├── PrivacyConsentDialog/
│ │ │ │ │ ├── index.css
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── PrivacyControlDialog/
│ │ │ │ │ ├── index.css
│ │ │ │ │ └── index.tsx
│ │ │ │ └── VideoJSPlayer/
│ │ │ │ ├── index.tsx
│ │ │ │ └── videojs-contain.css
│ │ │ ├── config/
│ │ │ │ └── i18n.ts
│ │ │ ├── constants/
│ │ │ │ ├── appConstants.ts
│ │ │ │ └── styleConstants.ts
│ │ │ ├── containers/
│ │ │ │ ├── ConnectionPrompts/
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── MainView/
│ │ │ │ │ ├── ConnectionIconEnum.ts
│ │ │ │ │ ├── LoadingSharingIconEnum.ts
│ │ │ │ │ ├── changeLanguage.ts
│ │ │ │ │ ├── handleCreatePeerConnection.ts
│ │ │ │ │ ├── handleDisplayingLoadingSharingIconLoop.ts
│ │ │ │ │ ├── handleNoConnectionTimeout.ts
│ │ │ │ │ ├── handleRemoveDanglingReactRevealContainer.ts
│ │ │ │ │ ├── handleSetVideoQuality.ts
│ │ │ │ │ ├── index.css
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── useScreenViewingTracker.ts
│ │ │ │ └── PlayerView/
│ │ │ │ └── index.tsx
│ │ │ ├── features/
│ │ │ │ ├── PeerConnection/
│ │ │ │ │ ├── NullUser.ts
│ │ │ │ │ ├── PartnerPeerUser.d.ts
│ │ │ │ │ ├── PeerConnection.d.ts
│ │ │ │ │ ├── PeerConnectionUIHandler.ts
│ │ │ │ │ ├── ReceiveEncryptedMessagePayload.d.ts
│ │ │ │ │ ├── ScreenSharingSourceEnum.ts
│ │ │ │ │ ├── errors/
│ │ │ │ │ │ ├── PeerConnectionPartnerIsNotDefinedError.ts
│ │ │ │ │ │ ├── PeerConnectionPeerIsNullError.ts
│ │ │ │ │ │ ├── PeerConnectionSocketNotDefined.ts
│ │ │ │ │ │ └── PeerConnectionUserIsNotDefinedError.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── mocks/
│ │ │ │ │ │ ├── INPUTtestWindowNavigatorUserAgent.ts
│ │ │ │ │ │ ├── INPUTvideo500000testSdpMediaBitrate.ts
│ │ │ │ │ │ ├── OUTPUTDeviceDetailsFromUAParsed.ts
│ │ │ │ │ │ └── OUTPUTvideo500000testSdpMediaBitrate.ts
│ │ │ │ │ ├── peerConnectionHandlePeer.ts
│ │ │ │ │ ├── peerConnectionHandleSocket.ts
│ │ │ │ │ ├── peerConnectionReceiveEncryptedMessage.ts
│ │ │ │ │ ├── setAndShowErrorDialogMessage.ts
│ │ │ │ │ ├── setSdpMediaBitrate.ts
│ │ │ │ │ ├── simplePeerDataMessages.ts
│ │ │ │ │ └── startSocketConnectedCheckingLoop/
│ │ │ │ │ └── index.ts
│ │ │ │ └── VideoAutoQualityOptimizer/
│ │ │ │ ├── VideoQualityEnum.ts
│ │ │ │ ├── errors/
│ │ │ │ │ ├── CanvasNotDefinedError.ts
│ │ │ │ │ ├── ImageDataIsUndefinedError.ts
│ │ │ │ │ ├── VideoDimensionsAreWrongError.ts
│ │ │ │ │ └── VideoNotDefinedError.ts
│ │ │ │ └── index.ts
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ ├── providers/
│ │ │ │ └── AppContextProvider/
│ │ │ │ └── index.tsx
│ │ │ ├── utils/
│ │ │ │ ├── ProcessedMessage.d.ts
│ │ │ │ ├── analytics.ts
│ │ │ │ ├── gaRequestInterceptor.ts
│ │ │ │ ├── message.ts
│ │ │ │ ├── playerFullscreen.ts
│ │ │ │ ├── socket.ts
│ │ │ │ └── userAgentParserHelpers.ts
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── common/
│ │ ├── DesktopCapturerSourceType.ts
│ │ ├── Device.ts
│ │ ├── ElectronStoreKeys.enum.ts
│ │ ├── IpcEvents.enum.ts
│ │ ├── LocalPeerUser.ts
│ │ ├── SendEncryptedMessagePayload.ts
│ │ ├── app.lang.config.ts
│ │ ├── config.ts
│ │ ├── connectSocket.ts
│ │ ├── deskreen-electron-store.ts
│ │ ├── getAppLanguage.ts
│ │ ├── isProduction.ts
│ │ ├── locales/
│ │ │ ├── da/
│ │ │ │ └── translation.json
│ │ │ ├── de/
│ │ │ │ └── translation.json
│ │ │ ├── en/
│ │ │ │ └── translation.json
│ │ │ ├── es/
│ │ │ │ └── translation.json
│ │ │ ├── fi/
│ │ │ │ └── translation.json
│ │ │ ├── fr/
│ │ │ │ └── translation.json
│ │ │ ├── it/
│ │ │ │ └── translation.json
│ │ │ ├── ja/
│ │ │ │ └── translation.json
│ │ │ ├── ko/
│ │ │ │ └── translation.json
│ │ │ ├── nl/
│ │ │ │ └── translation.json
│ │ │ ├── ru/
│ │ │ │ └── translation.json
│ │ │ ├── sv/
│ │ │ │ └── translation.json
│ │ │ ├── ua/
│ │ │ │ └── translation.json
│ │ │ ├── zh_CN/
│ │ │ │ └── translation.json
│ │ │ └── zh_TW/
│ │ │ └── translation.json
│ │ └── rateLimitedConsole.ts
│ ├── features/
│ │ ├── ConnectedDevicesService/
│ │ │ └── index.ts
│ │ ├── DesktopCapturerSourcesService/
│ │ │ └── index.ts
│ │ ├── PeerConnectionHelperRendererService/
│ │ │ └── index.ts
│ │ └── SharingSessionService/
│ │ ├── SharingSession.ts
│ │ ├── SharingSessionStatusEnum.ts
│ │ ├── SharingTypeEnum.ts
│ │ └── index.ts
│ ├── main/
│ │ ├── configs/
│ │ │ └── i18next.config.ts
│ │ ├── helpers/
│ │ │ ├── getDeskreenGlobal.ts
│ │ │ ├── getMyLocalIpV4.ts
│ │ │ ├── initGlobals.ts
│ │ │ ├── ipcMainHandlers.ts
│ │ │ └── isWifiConnected.ts
│ │ ├── index.ts
│ │ ├── menu.ts
│ │ └── utils/
│ │ ├── LoggerWithFilePrefix.ts
│ │ ├── getNewVersionTag.ts
│ │ ├── installExtensions.ts
│ │ └── isLinuxWaylandSession.ts
│ ├── preload/
│ │ ├── index.d.ts
│ │ └── index.ts
│ ├── renderer/
│ │ ├── index.html
│ │ ├── peerConnectionHelperRendererWindowIndex.html
│ │ └── src/
│ │ ├── assets/
│ │ │ ├── base.css
│ │ │ └── main.css
│ │ ├── components/
│ │ │ ├── AllowConnectionForDeviceAlert.tsx
│ │ │ ├── CloseOverlayButton.tsx
│ │ │ ├── ConnectedDevicesListDrawer.tsx
│ │ │ ├── DeviceInfoCallout/
│ │ │ │ └── index.tsx
│ │ │ ├── LanguageSelector/
│ │ │ │ └── index.tsx
│ │ │ ├── SettingsOverlay/
│ │ │ │ ├── SettingRowLabelAndInput.tsx
│ │ │ │ ├── SettingsOverlay.tsx
│ │ │ │ └── settings-overlay.css
│ │ │ ├── ShareAppOrScreenControlGroup.tsx
│ │ │ ├── SharingSourcePreviewCard/
│ │ │ │ └── index.tsx
│ │ │ ├── StepperPanel/
│ │ │ │ ├── ColorlibConnector.tsx
│ │ │ │ ├── ColorlibStepIcon.tsx
│ │ │ │ └── DeviceConnectedInfoButton.tsx
│ │ │ ├── StepsOfStepper/
│ │ │ │ ├── ChooseAppOrScreenOverlay/
│ │ │ │ │ ├── ChooseAppOrScreenOverlay.tsx
│ │ │ │ │ ├── PreviewGridList.tsx
│ │ │ │ │ └── ViewSharingObject.d.ts
│ │ │ │ ├── ChooseAppOrScreenStep.tsx
│ │ │ │ ├── ConfirmStep.tsx
│ │ │ │ ├── IntermediateStep.tsx
│ │ │ │ ├── ScanQRStep.tsx
│ │ │ │ └── SuccessStep.tsx
│ │ │ ├── TopPanel.tsx
│ │ │ └── css.d.ts
│ │ ├── configs/
│ │ │ └── i18next.config.client.ts
│ │ ├── containers/
│ │ │ ├── App.tsx
│ │ │ ├── DeskreenStepper.tsx
│ │ │ ├── HomePage.tsx
│ │ │ ├── Root.tsx
│ │ │ └── SettingsProvider.tsx
│ │ ├── contexts/
│ │ │ └── SettingsContext.tsx
│ │ ├── env.d.ts
│ │ ├── features/
│ │ │ └── PeerConnection/
│ │ │ ├── NullSimplePeer.ts
│ │ │ ├── NullUser.ts
│ │ │ ├── PartnerPeerUser.d.ts
│ │ │ ├── PeerConnection.d.ts
│ │ │ ├── ReceiveEncryptedMessagePayload.d.ts
│ │ │ ├── createDesktopCapturerStream.ts
│ │ │ ├── getDesktopSourceStreamBySourceID.ts
│ │ │ ├── handleCreatePeer.ts
│ │ │ ├── handlePeerOnData.ts
│ │ │ ├── handleSelfDestroy.ts
│ │ │ ├── handleSetDisplaySizeFromLocalStream.ts
│ │ │ ├── handleSocket.ts
│ │ │ ├── handleSocketUserEnter.ts
│ │ │ ├── handleSocketUserExit.ts
│ │ │ ├── index.ts
│ │ │ ├── prepareDataMessageToSendScreenSourceType.ts
│ │ │ ├── setSdpMediaBitrate.ts
│ │ │ └── simplePeerHandleSdpTransform.ts
│ │ ├── main.tsx
│ │ ├── peerConnectionHelperRendererWindowIndex.tsx
│ │ └── utils/
│ │ ├── handleRecieveEncryptedMessage.ts
│ │ ├── message.ts
│ │ └── showMessageFromNewToaster.ts
│ └── server/
│ ├── Room.d.ts
│ ├── RoomIDService/
│ │ └── index.ts
│ ├── darkwireSocket.ts
│ ├── getClientViewerDistPath.ts
│ ├── index.ts
│ ├── onDeviceConnectedCallback.ts
│ ├── socketsIPService.ts
│ ├── startPollForInactiveRooms.ts
│ └── store/
│ ├── MemoryStore.ts
│ ├── index.ts
│ └── socketIOServerStore.ts
├── tsconfig.json
├── tsconfig.node.json
├── tsconfig.web.json
└── wiki/
└── Home.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# TODO: edit funding when ready
# These are supported funding model platforms
# github: [deskreen, pavlobu]
patreon: deskreen
open_collective: deskreen
github: pavlobu
================================================
FILE: .github/ISSUE_TEMPLATE/1-Bug_report.md
================================================
---
name: Bug report
about: You're having technical issues. 🐞 And willing to share more details. If you don't know details, write here https://github.com/pavlobu/deskreen/discussions/68
labels: 'bug'
---
<!-- Please use the following issue template or your issue will be closed -->
## Prerequisites
<!-- If the following boxes are not ALL checked, your issue is likely to be closed -->
- [ ] Using yarn
- [ ] Using an up-to-date [`master` branch](https://github.com/pavlobu/deskreen/tree/master)
- [ ] Using latest version of devtools. [Check the docs for how to update](https://electron-react-boilerplate.js.org/docs/dev-tools/)
- [ ] For issue in production release, add devtools output of `DEBUG_PROD=true yarn build && yarn start`
## Expected Behavior
<!--- What should have happened? -->
## Current Behavior
<!--- What went wrong? -->
## Steps to Reproduce
<!-- Add relevant code and/or a live example -->
<!-- Add stack traces -->
1.
2.
3.
4.
## Possible Solution (Not obligatory)
<!--- Suggest a reason for the bug or how to fix it. -->
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Did you make any changes to the boilerplate after cloning it? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
- Node version :
- Deskreen version or branch :
- Operating System and version :
- Link to your project :
<!---
❗️❗️ Also, please consider donating (https://opencollective.com/deskreen) ❗️❗️
Donations will ensure the following:
🔨 Long term maintenance of the project
🛣 Progress on the roadmap
🐛 Quick responses to bug reports and help requests
-->
================================================
FILE: .github/ISSUE_TEMPLATE/3-Feature_request.md
================================================
---
name: Feature request
about: You want something small added to Deskreen and with concrete code and solution. 🎉 If it is a big enhancement drop it here https://github.com/pavlobu/deskreen/discussions/50
labels: 'enhancement'
---
# Here you post only concrete examples of enhancements with code and solutions that you have. Other BIG enhancements and general ideas of features you would like to see in Deskreen, you post here: https://github.com/pavlobu/deskreen/discussions/50
# Otherwise this issue will be closed.
<!---
❗️❗️ Also, please consider donating (https://opencollective.com/deskreen) ❗️❗️
Donations will ensure the following:
🔨 Long term maintenance of the project
🛣 Progress on the roadmap
🐛 Quick responses to bug reports and help requests
-->
================================================
FILE: .github/config.yml
================================================
requiredHeaders:
- Prerequisites
- Expected Behavior
- Current Behavior
- Possible Solution
- Your Environment
================================================
FILE: .github/stale.yml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pr
- discussion
- e2e
- enhancement
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
================================================
FILE: .github/workflows/release.yml
================================================
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
name: release all os — mac signed & notarized, windows, linux
permissions:
contents: write # Required to create releases and upload assets
jobs:
create-release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Create GitHub Release (draft)
id: create_release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
tag="${{ github.ref_name }}"
echo "Ensuring draft release for tag: $tag"
if gh release view "$tag" >/dev/null 2>&1; then
echo "Release $tag already exists, skipping creation"
else
gh release create "$tag" \
--title "$tag" \
--draft \
--notes ""
fi
release:
name: Release (${{ matrix.build_name }})
needs: create-release
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-latest
platform: linux
build_name: linux-x64
build_command: pnpm build:linux
artifact_name: 'dist/{*.AppImage,*.rpm,*.deb,*.yml}'
- os: ubuntu-latest
platform: linux
build_name: linux-arm64
build_command: pnpm build:linux:arm64
artifact_name: 'dist/{*.AppImage,*.rpm,*.deb,*.yml}'
- os: ubuntu-latest
platform: linux
build_name: linux-arm32
build_command: pnpm build:linux:armv7l
artifact_name: 'dist/{*.AppImage,*.rpm,*.deb,*.yml}'
- os: windows-latest
platform: windows
build_name: windows-amd64
build_command: pnpm build:win
artifact_name: 'dist/{*.msi,*.exe,*.blockmap,*.yml}'
- os: windows-latest
platform: windows
build_name: windows-amd32
build_command: pnpm build:win:ia32
artifact_name: 'dist/{*.msi,*.exe,*.blockmap,*.yml}'
- os: windows-latest
platform: windows
build_name: windows-arm64
build_command: pnpm build:win:arm64
artifact_name: 'dist/{*.msi,*.exe,*.blockmap,*.yml}'
- os: macos-latest
platform: macos
build_name: macos-arm64
build_command: pnpm build:mac:arm64
artifact_name: 'dist/{*.dmg,*.blockmap,*.yml}'
- os: macos-15-intel
platform: macos
build_name: macos-intel
build_command: pnpm build:mac:x64
artifact_name: 'dist/{*.dmg,*.blockmap,*.yml}'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js environment
uses: actions/setup-node@v4
with:
node-version: '23'
- name: Install pnpm
run: npm install -g pnpm
- name: pnpm install in ./src/client-viewer
run: |
cd ./src/client-viewer
pnpm install
- name: pnpm install in ./
run: pnpm install
- name: Install Linux build dependencies
if: ${{ matrix.platform == 'linux' }}
run: |
sudo apt-get update
sudo apt-get install -y libfuse2
- name: Create .env file for client-viewer with Google Analytics tag (macOS)
if: ${{ matrix.platform == 'macos' }}
run: |
echo "VITE_CLIENT_VIEWER_GA_TAG=${{ secrets.CLIENT_VIEWER_GA_TAG_MACOS }}" > ./src/client-viewer/.env
- name: Create .env file for client-viewer with Google Analytics tag (Windows)
if: ${{ matrix.platform == 'windows' }}
run: |
echo "VITE_CLIENT_VIEWER_GA_TAG=${{ secrets.CLIENT_VIEWER_GA_TAG_WINDOWS }}" > ./src/client-viewer/.env
- name: Create .env file for client-viewer with Google Analytics tag (Linux)
if: ${{ matrix.platform == 'linux' }}
run: |
echo "VITE_CLIENT_VIEWER_GA_TAG=${{ secrets.CLIENT_VIEWER_GA_TAG_LINUX }}" > ./src/client-viewer/.env
- name: pnpm build on Windows
if: ${{ matrix.platform == 'windows' }}
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
run: |
# 1. Pre-install the Azure Trusted Signing module
# This prevents electron-builder from running the buggy 'install' sub-routine
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module -Name TrustedSigning -AllowPrerelease -Force -Scope CurrentUser
$ErrorActionPreference = 'Stop'
$attempts = 3
for ($i = 1; $i -le $attempts; $i++) {
Write-Host "Build attempt $($i)/$($attempts)"
# Execute the pnpm script directly in the shell
# We use 'pnpm run' instead of 'cmd /c' to avoid extra shell escaping layers
pnpm run ${{ matrix.build_command == 'pnpm build:win' && 'build:win' || (matrix.build_command == 'pnpm build:win:ia32' && 'build:win:ia32' || 'build:win:arm64') }}
if ($LASTEXITCODE -eq 0) { exit 0 }
if ($i -lt $attempts) {
Write-Host "Build failed. Retrying in 20s..."
Start-Sleep -Seconds 20
}
}
exit $LASTEXITCODE
- name: pnpm build on macOS
if: ${{ matrix.platform == 'macos' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# certificate for signing (base64-encoded .p12) and its password
CSC_LINK: ${{ secrets.MAC_CERTS }}
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
run: ${{ matrix.build_command }}
- name: pnpm build on Linux
if: ${{ matrix.platform == 'linux' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ${{ matrix.build_command }}
- name: Rename Linux update manifest for non-x64
if: ${{ matrix.platform == 'linux' }}
run: |
if [ -f dist/latest-linux.yml ]; then
mv dist/latest-linux.yml dist/latest-${{ matrix.build_name }}.yml
fi
- name: Rename Windows update manifest for non-x64
if: ${{ matrix.platform == 'windows' }}
shell: pwsh
run: |
$manifest = Join-Path dist 'latest.yml'
if (Test-Path $manifest) {
Rename-Item $manifest "latest-${{ matrix.build_name }}.yml"
}
- name: Rename macOS update manifest for arm builds
if: ${{ matrix.platform == 'macos' }}
run: |
if [ -f dist/latest-mac.yml ]; then
mv dist/latest-mac.yml dist/latest-${{ matrix.build_name }}.yml
fi
- name: Notarize and staple macOS DMG
if: ${{ matrix.platform == 'macos' }}
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
shopt -s nullglob
for dmg in dist/*.dmg; do
echo "Submitting $dmg for notarization"
xcrun notarytool submit "$dmg" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
--wait
echo "Stapling ticket to $dmg"
xcrun stapler staple "$dmg"
done
- name: Upload binaries to release
uses: xresloader/upload-to-github-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
file: ${{ matrix.artifact_name }}
tags: true
draft: true
================================================
FILE: .gitignore
================================================
.history
node_modules
dist
out
.DS_Store
.pnpm-store
.eslintcache
*.log*
src/client-viewer/dist
src/client-viewer/node_modules
.idea
.env
tsconfig.web.tsbuildinfo
src/client-viewer/.env
AGENTS.md
================================================
FILE: CHANGELOG.md
================================================
# 1.0.11 (8 Mar 2021)
Changes:
- add Danish language translations. Special thanks to @LauritsLL
- add German language translations. Special thanks to @Aceto1
<br/>
<br/>
# 1.0.10 (25 Feb 2021)
Changes:
- add Spanish language translations. Special thanks to @schiappa
<br/>
<br/>
# 1.0.9 (24 Feb 2021)
Changes:
- remove severe Windows UI lags.
- Increase performance on weak Windows machines.
<br/>
<br/>
# 1.0.8 (23 Feb 2021)
Changes:
- added locales for Traditional Chinese language, special thanks to @taotieren
- minor UI improvements
<br/>
<br/>
# 1.0.7 (21 Feb 2021)
Changes:
- added locales for Chinese language, special thanks to @taotieren
- minor UI improvements
<br/>
<br/>
# 1.0.6 (20 Feb 2021)
Changes:
- added locales for Russian and Ukrainian languages
- minor UI improvements
<br/>
<br/>
# 1.0.5 (7 Feb 2021)
Changes:
- Fix quality is not set to 100% when sharing started: https://github.com/pavlobu/deskreen/issues/100
<br/>
<br/>
# 1.0.4 (4 Feb 2021)
Changes:
- add Flip button in client web view (gear icon button, next to play button) for use a tablet as a teleprompter
- set 100% video quality by default when screen sharing starts
- add polyfills to remove issue of blank pages on old browsers. Thanks @klarkc
- display error message in client viewer if there is WebRTC error
<br/>
<br/>
# 1.0.3 (29 Jan 2021)
Changes:
- remove pulsing animation on orange step bubbles
- portable version on windows
- release draft before publishing release
- remove non-working macOS zip from release
<br/>
<br/>
# 1.0.2 (27 Jan 2021)
Changes:
- Security patches
- Updated electron version to 11.2.1
- fix for https://github.com/pavlobu/deskreen/issues/45
- ? fix for https://github.com/pavlobu/deskreen/issues/56
- ? fix for https://github.com/pavlobu/deskreen/issues/17
<br/>
<br/>
# 1.0.1 (25 Jan 2021)
Changes:
- Fix typos in Deskreen. Special thanks to @EdwardBetts
<br/>
<br/>
# 1.0.0 (18 Jan 2021)
Features:
- works with WiFi or LAN
- use any device with web browser as second screen for your computer (using Display Dummy Plug)
- use any device web browser to mirror your computer's screen
- use any device web browser to view a single application window from your computer's screen
- supports multiple screen sharing sessions to as many devices as you want
- supports changing picture quality while sharing a screen.
- Picture auto quality change supported. (for performance boost while watching youtube video for example)
- End-to-end security
- dark mode UI support
- available for Win / Mac / Linux
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at pavlobu@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: LICENSE
================================================
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
================================================
FILE: README.md
================================================
# Deskreen CE (Community Edition)

(Over 2M downloads during 5 years since launch)

## Deskreen turns any device with a web browser into a secondary screen for your computer
## To learn more visit our website: [deskreen.com](https://deskreen.com)
## [Donate to support Deskreen Open-Source](https://deskreen.com/#contribute)
Deskreen is an `electron.js` based application that uses `WebRTC` to make a live stream of your computer screen to a web browser on any device. It is available for MacOS, Windows and Linux operating systems.
The current open-source Community Edition version has limited features. If you need more features please consider upgrading to [Pro](https://deskreen.com/download) version for more features when it is released.
---
### ▶️ [See how people use Deskreen on Youtube](https://www.youtube.com/results?search_query=deskreen) (video tutorials, demos, use cases for Deskreen day to day usage)
---
## [Deskreen Frequently Asked Questions](https://deskreen.com/faq)
---
### Prerequisites
You will need to have `node>=v23` `pnpm>=v10.20.0` installed.
1. git clone this repo
2. `pnpm i`
3. `cd ./src/client-viewer && pnpm i && cd ../..`
4. `pnpm clean && pnpm build && pnpm start` -- run in prod like mode
#### for more pnpm commands look at `package.json`
## Starting with Custom Local IP
You can start Deskreen CE with a custom local IP address using the `--local-ip` or `--ip` CLI flag. This is useful when you want to specify a particular network interface IP address.
### macOS
```bash
# Using open command (recommended)
open -a "Deskreen CE" --args --ip 192.168.1.100
# Or using the executable directly
/Applications/Deskreen\ CE.app/Contents/MacOS/Deskreen\ CE --ip 192.168.1.100
# Get your IP automatically and launch
open -a "Deskreen CE" --args --ip "192.168.1.100"
```
### Windows
```powershell
# Using Start-Process (PowerShell)
Start-Process "Deskreen CE" -ArgumentList "--ip", "192.168.1.100"
# Or using the executable directly
"C:\Program Files\Deskreen CE\Deskreen CE.exe" --ip 192.168.1.100
# Or from Command Prompt
start "" "C:\Program Files\Deskreen CE\Deskreen CE.exe" --ip 192.168.1.100
```
### Linux
```bash
# If installed via AppImage
./Deskreen\ CE-*.AppImage --ip 192.168.1.100
# If installed via .deb/.rpm package (usually in /usr/bin or /opt)
deskreen-ce --ip 192.168.1.100
# Or using full path
/opt/Deskreen\ CE/deskreen-ce --ip 192.168.1.100
```
**Note:** Replace `192.168.1.100` with your actual local IP address. You can find your IP using:
- **macOS/Linux:** `ipconfig getifaddr en0` or `ifconfig | grep "inet "`
- **Windows:** `ipconfig` (look for IPv4 Address)
When using the `--ip` or `--local-ip` flag, the app will use the specified IP for QR codes and connection URLs, while still monitoring the actual network interface status for WiFi connection detection.
## Maintainer
- [Pavlo (Paul) Buidenkov](https://www.linkedin.com/in/pavlobu)
## License
AGPL-3.0 License © [Pavlo (Paul) Buidenkov](https://github.com/pavlobu/deskreen)
## Copyright
Electron-Vite MIT License © [electron-vite](https://github.com/alex8088/electron-vite)
React MIT License © [Facebook, Inc. and its affiliates](https://github.com/facebook/react)
Vite MIT License © [Vite.js](https://github.com/vitejs/vite)
Electron Builder MIT License © [electron-builder contributors](https://github.com/electron-userland/electron-builder)
Apache 2.0 © [blueprintjs](https://github.com/palantir/blueprint)
simple-peer MIT. Copyright © [Feross Aboukhadijeh](http://feross.org/)
tweetnacl ISC License © Dmitry Chestnykh, Devi Mandiri, and contributors (https://github.com/dchest/tweetnacl-js)
darkwire.io MIT License © [darkwire/darkwire.io](https://github.com/darkwire/darkwire.io)
And many many others...
## Thanks
🙏 Many thanks to all 🌍 open source community members and maintainers of libraries used in this project.
================================================
FILE: biome.json
================================================
{
"$schema": "https://biomejs.dev/schemas/2.3.6/schema.json",
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
"files": { "includes": ["**", "!!**/dist"] },
"formatter": { "enabled": true, "indentStyle": "tab" },
"linter": {
"enabled": true,
"rules": {
"recommended": false,
"complexity": {
"noAdjacentSpacesInRegex": "error",
"noExtraBooleanCast": "error",
"noUselessCatch": "error",
"noUselessEscapeInRegex": "error",
"noUselessTypeConstraint": "error"
},
"correctness": {
"noChildrenProp": "error",
"noConstAssign": "error",
"noConstantCondition": "error",
"noEmptyCharacterClassInRegex": "error",
"noEmptyPattern": "error",
"noGlobalObjectCalls": "error",
"noInvalidBuiltinInstantiation": "error",
"noInvalidConstructorSuper": "error",
"noNonoctalDecimalEscape": "error",
"noPrecisionLoss": "error",
"noSelfAssign": "error",
"noSetterReturn": "error",
"noSwitchDeclarations": "error",
"noUndeclaredVariables": "error",
"noUnreachable": "error",
"noUnreachableSuper": "error",
"noUnsafeFinally": "error",
"noUnsafeOptionalChaining": "error",
"noUnusedLabels": "error",
"noUnusedPrivateClassMembers": "error",
"noUnusedVariables": "error",
"useIsNan": "error",
"useJsxKeyInIterable": "error",
"useValidForDirection": "error",
"useValidTypeof": "error",
"useYield": "error"
},
"security": { "noDangerouslySetInnerHtmlWithChildren": "error" },
"style": {
"noCommonJs": "error",
"noNamespace": "error",
"noNonNullAssertion": "off",
"useArrayLiterals": "error",
"useAsConstAssertion": "error",
"useBlockStatements": "off"
},
"suspicious": {
"noAsyncPromiseExecutor": "error",
"noCatchAssign": "error",
"noClassAssign": "error",
"noCommentText": "error",
"noCompareNegZero": "error",
"noConstantBinaryExpressions": "error",
"noControlCharactersInRegex": "error",
"noDebugger": "error",
"noDuplicateCase": "error",
"noDuplicateClassMembers": "error",
"noDuplicateElseIf": "error",
"noDuplicateJsxProps": "error",
"noDuplicateObjectKeys": "error",
"noDuplicateParameters": "error",
"noEmptyBlockStatements": "error",
"noExplicitAny": "error",
"noExtraNonNullAssertion": "error",
"noFallthroughSwitchClause": "error",
"noFunctionAssign": "error",
"noGlobalAssign": "error",
"noImportAssign": "error",
"noIrregularWhitespace": "error",
"noMisleadingCharacterClass": "error",
"noMisleadingInstantiator": "error",
"noNonNullAssertedOptionalChain": "error",
"noPrototypeBuiltins": "error",
"noRedeclare": "error",
"noShadowRestrictedNames": "error",
"noSparseArray": "error",
"noUnsafeDeclarationMerging": "error",
"noUnsafeNegation": "error",
"noUselessRegexBackrefs": "error",
"noWith": "error",
"useGetterReturn": "error",
"useNamespaceKeyword": "error"
}
},
"includes": ["**", "!**/node_modules", "!**/dist", "!**/out"]
},
"javascript": {
"formatter": { "quoteStyle": "single" },
"globals": [
"onanimationend",
"exports",
"ongamepadconnected",
"onlostpointercapture",
"onanimationiteration",
"onkeyup",
"onmousedown",
"onanimationstart",
"onslotchange",
"onprogress",
"ontransitionstart",
"onpause",
"onended",
"onpointerover",
"onscrollend",
"onformdata",
"ontransitionrun",
"onanimationcancel",
"ondrag",
"onchange",
"onbeforeinstallprompt",
"onbeforexrselect",
"onmessage",
"ontransitioncancel",
"onpointerdown",
"onabort",
"onpointerout",
"oncuechange",
"ongotpointercapture",
"onscrollsnapchanging",
"onsearch",
"onsubmit",
"onstalled",
"onsuspend",
"onreset",
"onerror",
"onresize",
"onmouseenter",
"ongamepaddisconnected",
"ondragover",
"onbeforetoggle",
"onmouseover",
"onpagehide",
"onmousemove",
"onratechange",
"oncommand",
"onmessageerror",
"onwheel",
"ondevicemotion",
"onauxclick",
"ontransitionend",
"onpaste",
"onpageswap",
"ononline",
"ondeviceorientationabsolute",
"onkeydown",
"onclose",
"onselect",
"onpageshow",
"onpointercancel",
"onbeforematch",
"onpointerrawupdate",
"ondragleave",
"onscrollsnapchange",
"onseeked",
"onwaiting",
"onbeforeunload",
"onplaying",
"onvolumechange",
"ondragend",
"onstorage",
"onloadeddata",
"onfocus",
"onoffline",
"onplay",
"onafterprint",
"onclick",
"oncut",
"onmouseout",
"ondblclick",
"oncanplay",
"onloadstart",
"onappinstalled",
"onpointermove",
"ontoggle",
"oncontextmenu",
"onblur",
"oncancel",
"onbeforeprint",
"oncontextrestored",
"onloadedmetadata",
"onpointerup",
"onlanguagechange",
"oncopy",
"onselectstart",
"onscroll",
"onload",
"ondragstart",
"onbeforeinput",
"oncanplaythrough",
"oninput",
"oninvalid",
"ontimeupdate",
"ondurationchange",
"onselectionchange",
"onmouseup",
"location",
"onkeypress",
"onpointerleave",
"oncontextlost",
"ondrop",
"onsecuritypolicyviolation",
"oncontentvisibilityautostatechange",
"ondeviceorientation",
"onseeking",
"onrejectionhandled",
"onunload",
"onmouseleave",
"onhashchange",
"onpointerenter",
"onmousewheel",
"onunhandledrejection",
"ondragenter",
"onpopstate",
"onpagereveal",
"onemptied"
]
},
"overrides": [
{
"includes": ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"],
"linter": {
"rules": {
"complexity": { "noArguments": "error" },
"correctness": {
"noConstAssign": "off",
"noGlobalObjectCalls": "off",
"noInvalidBuiltinInstantiation": "off",
"noInvalidConstructorSuper": "off",
"noSetterReturn": "off",
"noUndeclaredVariables": "off",
"noUnreachable": "off",
"noUnreachableSuper": "off"
},
"style": { "useConst": "error" },
"suspicious": {
"noClassAssign": "off",
"noDuplicateClassMembers": "off",
"noDuplicateObjectKeys": "off",
"noDuplicateParameters": "off",
"noFunctionAssign": "off",
"noImportAssign": "off",
"noRedeclare": "off",
"noUnsafeNegation": "off",
"noVar": "error",
"noWith": "off",
"useGetterReturn": "off"
}
}
}
},
{
"includes": ["scripts/**/*.js"],
"linter": {
"rules": {
"style": { "noCommonJs": "off" }
}
}
},
{ "includes": ["*.js", "*.mjs"], "linter": { "rules": {} } },
{
"includes": ["**/*.{ts,tsx}"],
"linter": {
"rules": {
"correctness": {
"useExhaustiveDependencies": "warn",
"useHookAtTopLevel": "error"
}
}
}
}
],
"assist": {
"enabled": true,
"actions": { "source": { "organizeImports": "on" } }
}
}
================================================
FILE: build/entitlements.mac.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>
================================================
FILE: dev-app-update.yml
================================================
provider: generic
url: https://example.com/auto-updates
updaterCacheDirName: deskreen-ce-updater
================================================
FILE: electron-builder.yml
================================================
appId: com.deskreen-ce.app
productName: Deskreen CE
directories:
buildResources: build
files:
- '!**/.vscode/*'
- '!src/*'
- '!electron.vite.config.{js,ts,mjs,cjs}'
- '!{.eslintcache,eslint.config.mjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md,AGENTS.md}'
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
extraResources:
- from: out/client-viewer
to: client-viewer
asarUnpack:
- resources/**
win:
artifactName: ${name}-${version}-${arch}.${ext}
executableName: Deskreen CE
target:
- portable
- msi
nsis: null
mac:
artifactName: ${name}-${version}-${arch}.${ext}
hardenedRuntime: true
entitlements: build/entitlements.mac.plist
entitlementsInherit: build/entitlements.mac.plist
extendInfo: []
notarize: false
dmg:
artifactName: ${name}-${version}-${arch}.${ext}
linux:
target:
- AppImage
- rpm
- deb
# - snap
maintainer: electronjs.org
category: Utility
appImage:
artifactName: ${name}-${version}-${arch}.${ext}
npmRebuild: false
publish:
provider: generic
url: https://example.com/auto-updates
electronDownload:
mirror: https://npmmirror.com/mirrors/electron/
================================================
FILE: electron.vite.config.ts
================================================
import { resolve } from 'path';
import {
defineConfig,
externalizeDepsPlugin,
bytecodePlugin,
} from 'electron-vite';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import react from '@vitejs/plugin-react';
import fs from 'fs-extra';
// Custom Vite plugin to copy the 'client-viewer/dist' directory
const copyClientViewerStaticFiles = () => {
return {
name: 'copy-client-viewer-static-files', // A unique name for your plugin
// The 'writeBundle' hook runs after the bundles have been written to disk
async writeBundle() {
const sourceDir = resolve(__dirname, 'src/client-viewer/dist');
const destDir = resolve(__dirname, 'out/client-viewer');
console.log(`Attempting to copy static files from: ${sourceDir}`);
console.log(`To destination: ${destDir}`);
try {
// Ensure the destination directory exists and is empty before copying
await fs.emptyDir(destDir);
// Copy the entire contents of the source directory to the destination
await fs.copy(sourceDir, destDir);
console.log(
'Successfully copied client-viewer/dist to out/client-viewer',
);
} catch (err) {
console.error(`Error copying static files: ${err}`);
}
},
};
};
const copySimplePeerMinJsStaticFiles = () => {
return {
name: 'copy-simple-peer-min-js-static-files',
async writeBundle() {
const sourceFile = resolve(
__dirname,
'node_modules/simple-peer/simplepeer.min.js',
);
const destDir = resolve(__dirname, 'out/renderer/assets');
console.log(`Attempting to copy simple-peer.min.js from: ${sourceFile}`);
console.log(`To destination: ${destDir}`);
try {
// Ensure the destination directory exists
await fs.ensureDir(destDir);
// Copy the file to the destination
await fs.copyFile(sourceFile, resolve(destDir, 'simplepeer.min.js'));
console.log(
'Successfully copied simple-peer.min.js to out/client-viewer/static/js',
);
} catch (err) {
console.error(`Error copying simple-peer.min.js: ${err}`);
}
},
};
};
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin(), bytecodePlugin()],
},
preload: {
build: {
rollupOptions: {
input: {
index: resolve(__dirname, 'src/preload/index.ts'),
helperRenderer: resolve(__dirname, 'src/preload/index.ts'),
// webview: resolve(__dirname, 'src/preload/webview.js')
},
},
},
plugins: [externalizeDepsPlugin(), bytecodePlugin()],
},
renderer: {
build: {
rollupOptions: {
input: {
index: resolve(__dirname, 'src/renderer/index.html'),
helperRenderer: resolve(
__dirname,
'src/renderer/peerConnectionHelperRendererWindowIndex.html',
),
},
},
},
resolve: {
alias: {
'@renderer': resolve('src/renderer/src'),
'@common': resolve('src/common'),
},
},
plugins: [
react(),
copyClientViewerStaticFiles(),
copySimplePeerMinJsStaticFiles(),
],
},
});
================================================
FILE: package.json
================================================
{
"name": "deskreen-ce",
"version": "3.2.13",
"description": "Screen sharing and present screen: Turn any device into a secondary screen for your computer",
"main": "./out/main/index.js",
"author": "deskreen.com",
"homepage": "https://electron-vite.org",
"scripts": {
"clean": "rm -rf dist out src/client-viewer/dist",
"format": "biome format --write . --max-diagnostics=none",
"lint": "biome lint . --max-diagnostics=none",
"lint:error-only": "biome lint --write . --max-diagnostics=none --diagnostic-level=error",
"typecheck:client-viewer": "tsc --noEmit -p src/client-viewer/tsconfig.app.json --composite false",
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
"typecheck": "npm run typecheck:client-viewer && npm run typecheck:node && npm run typecheck:web",
"start": "electron-vite preview",
"dev": "concurrently \"pnpm electron-vite dev\" \"pnpm:devClientViewer\"",
"devClientViewer": "cd src/client-viewer && pnpm dev --host --port=5174",
"buildDev": "npm run buildClientViewer && electron-vite build",
"build": "npm run typecheck && npm run buildClientViewer && electron-vite build",
"buildClientViewer": "cd src/client-viewer && pnpm build",
"postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir",
"build:win": "npm run build && electron-builder --win",
"build:win:ia32": "npm run build && electron-builder --win --ia32",
"build:win:arm64": "npm run build && electron-builder --win --arm64",
"build:mac": "npm run buildClientViewer && electron-vite build && electron-builder --mac",
"build:mac:arm64": "npm run buildClientViewer && electron-vite build && electron-builder --mac --arm64",
"build:mac:x64": "npm run buildClientViewer && electron-vite build && electron-builder --mac --x64",
"build:linux": "npm run buildClientViewer && electron-vite build && electron-builder --linux",
"build:linux:arm64": "npm run buildClientViewer && electron-vite build && electron-builder --linux --arm64",
"build:linux:armv7l": "npm run buildClientViewer && electron-vite build && electron-builder --linux --armv7l"
},
"dependencies": {
"@blueprintjs/core": "^6.3.4",
"@blueprintjs/select": "^6.0.8",
"@electron-toolkit/preload": "^3.0.2",
"@electron-toolkit/utils": "^4.0.0",
"@fortawesome/fontawesome-free": "^7.1.0",
"@material-ui/core": "^4.12.4",
"@roamhq/wrtc": "^0.9.1",
"@types/lodash": "^4.17.20",
"axios": "^1.13.2",
"classnames": "^2.5.1",
"clsx": "^2.1.1",
"detect-port": "^2.1.0",
"electron-devtools-installer": "^4.0.0",
"electron-log": "^5.4.3",
"electron-settings": "^4.0.4",
"electron-store": "^10.1.0",
"electron-updater": "^6.6.2",
"fontsource-lexend-peta": "^4.0.0",
"fs-extra": "^11.3.2",
"get-port": "^7.1.0",
"i18next": "^25.6.2",
"i18next-fs-backend": "^2.6.0",
"i18next-sync-fs-backend": "^1.1.1",
"kcors": "^2.2.2",
"koa": "^3.1.1",
"koa-router": "^14.0.0",
"koa-send": "^5.0.1",
"koa-static": "^5.0.0",
"lodash": "^4.17.21",
"node-forge": "^1.3.1",
"normalize.css": "^8.0.1",
"qrcode.react": "^4.2.0",
"react-flexbox-grid": "^2.1.2",
"react-toast-notifications": "^2.5.1",
"shortid": "^2.2.17",
"simple-peer": "^9.11.1",
"socket.io": "^4.8.1",
"socket.io-client": "^4.8.1",
"uuid": "^11.1.0"
},
"devDependencies": {
"@biomejs/biome": "2.3.6",
"@electron-toolkit/tsconfig": "^1.0.1",
"@types/electron-devtools-installer": "^4.0.0",
"@types/i18next-node-fs-backend": "^2.1.5",
"@types/kcors": "^2.2.9",
"@types/koa": "^3.0.1",
"@types/koa-router": "^7.4.9",
"@types/koa-send": "^4.1.6",
"@types/koa-static": "^4.0.4",
"@types/node-forge": "^1.3.14",
"@types/qrcode.react": "^3.0.0",
"@types/react": "^19.2.3",
"@types/react-dom": "^19.2.2",
"@types/react-toast-notifications": "^2.4.1",
"@types/simple-peer": "^9.11.9",
"@types/socket.io": "^3.0.2",
"@types/socket.io-client": "^3.0.0",
"@vercel/blob": "^2.0.0",
"@vitejs/plugin-react": "^5.1.0",
"concurrently": "^9.2.1",
"electron": "^37.9.0",
"electron-builder": "^26.7.0",
"electron-vite": "^4.0.1",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-i18next": "^15.7.4",
"typescript": "^5.9.3",
"vite": "^7.2.2"
},
"pnpm": {
"onlyBuiltDependencies": [
"electron"
]
}
}
================================================
FILE: scripts/bump-version.js
================================================
#!/usr/bin/env node
'use strict';
const fs = require('node:fs/promises');
const path = require('node:path');
const { execSync, spawnSync } = require('node:child_process');
const rootDir = path.resolve(__dirname, '..');
const rootPackagePath = path.join(rootDir, 'package.json');
const clientPackagePath = path.join(
rootDir,
'src',
'client-viewer',
'package.json',
);
const envPath = path.join(rootDir, 'src', 'client-viewer', '.env');
const bumpFlags = new Map([
['--major', 'major'],
['--minor', 'minor'],
['--patch', 'patch'],
]);
const parsedArgs = parseArgs(process.argv.slice(2));
void main(parsedArgs).catch((error) => {
if (error instanceof Error) {
console.error(error.message);
} else {
console.error(error);
}
process.exit(1);
});
function parseArgs(argv) {
const matchedFlags = argv.filter((arg) => bumpFlags.has(arg));
if (matchedFlags.length === 0) {
throw new Error(
'Missing bump flag. Use one of --major, --minor, or --patch.',
);
}
if (matchedFlags.length > 1) {
throw new Error('Multiple bump flags provided. Please supply only one.');
}
return {
type: bumpFlags.get(matchedFlags[0]),
};
}
async function main({ type }) {
process.chdir(rootDir);
await ensureCleanGit();
const currentVersion = await readVersion();
const nextVersion = bumpVersion(currentVersion, type);
await Promise.all([
writeRootPackage(nextVersion),
writeClientPackage(nextVersion),
writeEnv(nextVersion),
]);
await stageFiles();
await commit(nextVersion);
await tag(nextVersion);
console.log(`Version bumped from ${currentVersion} to ${nextVersion}`);
}
async function ensureCleanGit() {
const output = execSync('git status --porcelain', {
encoding: 'utf8',
}).trim();
if (output.length > 0) {
throw new Error(
'Working tree is not clean. Please commit or stash changes before bumping the version.',
);
}
}
async function readVersion() {
const raw = await fs.readFile(rootPackagePath, 'utf8');
const parsed = JSON.parse(raw);
const version = parsed.version;
if (typeof version !== 'string') {
throw new Error('Root package.json does not contain a valid version.');
}
assertValidSemver(version);
return version;
}
async function writeRootPackage(version) {
await updatePackageJSON(rootPackagePath, version);
}
async function writeClientPackage(version) {
await updatePackageJSON(clientPackagePath, version);
}
async function updatePackageJSON(filePath, version) {
const raw = await fs.readFile(filePath, 'utf8');
const parsed = JSON.parse(raw);
parsed.version = version;
const value = `${JSON.stringify(parsed, null, 2)}\n`;
await fs.writeFile(filePath, value, 'utf8');
}
async function writeEnv(version) {
const raw = await fs.readFile(envPath, 'utf8');
const lines = raw.split(/\r?\n/);
let replaced = false;
const nextLines = lines.map((line) => {
if (line.startsWith('VITE_CLIENT_VIEWER_VERSION=')) {
replaced = true;
return `VITE_CLIENT_VIEWER_VERSION=${version}`;
}
return line;
});
if (!replaced) {
nextLines.push(`VITE_CLIENT_VIEWER_VERSION=${version}`);
}
await fs.writeFile(envPath, `${nextLines.join('\n')}\n`, 'utf8');
}
function bumpVersion(current, type) {
const [major, minor, patch] = current.split('.').map(Number);
if ([major, minor, patch].some((part) => Number.isNaN(part))) {
throw new Error(
`Unable to bump version. Invalid semantic version: ${current}`,
);
}
if (type === 'major') {
return `${major + 1}.0.0`;
}
if (type === 'minor') {
return `${major}.${minor + 1}.0`;
}
return `${major}.${minor}.${patch + 1}`;
}
function assertValidSemver(value) {
const semverPattern = /^(\d+)\.(\d+)\.(\d+)$/;
if (!semverPattern.test(value)) {
throw new Error(`Invalid semantic version: ${value}`);
}
}
async function stageFiles() {
execSync(`git add ${quote(rootPackagePath)} ${quote(clientPackagePath)}`, {
stdio: 'inherit',
});
}
async function commit(version) {
const message = version;
execSync(`git commit -m ${quote(message)}`, { stdio: 'inherit' });
}
async function tag(version) {
const tagName = `v${version}`;
const result = spawnSync('git', [
'show-ref',
'--tags',
'--quiet',
`refs/tags/${tagName}`,
]);
if (result.status === 0) {
throw new Error(`Tag ${tagName} already exists.`);
}
if (result.status !== 1) {
throw (
result.error ??
new Error(`git show-ref failed with status ${result.status ?? 'unknown'}`)
);
}
execSync(`git tag ${quote(tagName)}`, { stdio: 'inherit' });
}
function quote(value) {
return `'${value.replace(/'/g, "'\\''")}'`;
}
================================================
FILE: scripts/undo-version-bump.js
================================================
#!/usr/bin/env node
'use strict';
const fs = require('node:fs/promises');
const path = require('node:path');
const { execSync, spawnSync } = require('node:child_process');
const rootDir = path.resolve(__dirname, '..');
const rootPackagePath = path.join(rootDir, 'package.json');
const clientPackagePath = path.join(
rootDir,
'src',
'client-viewer',
'package.json',
);
const envPath = path.join(rootDir, 'src', 'client-viewer', '.env');
void main().catch((error) => {
if (error instanceof Error) {
console.error(error.message);
} else {
console.error(error);
}
process.exit(1);
});
async function main() {
process.chdir(rootDir);
await ensureCleanGit();
const latestTag = await getLatestVersionTag();
if (!latestTag) {
throw new Error('No version tag found. Nothing to undo.');
}
const tagVersion = latestTag.replace(/^v/, '');
console.log(
`Found latest version tag: ${latestTag} (version: ${tagVersion})`,
);
const tagCommit = await getTagCommit(latestTag);
const parentCommit = await getParentCommit(tagCommit);
if (!parentCommit) {
throw new Error(
'Cannot find parent commit. The version bump commit might be the first commit.',
);
}
const previousVersion = await getVersionFromCommit(parentCommit);
console.log(`Previous version: ${previousVersion}`);
await Promise.all([
writeRootPackage(previousVersion),
writeClientPackage(previousVersion),
writeEnv(previousVersion),
]);
await deleteRemoteTag(latestTag);
await deleteLocalTag(latestTag);
console.log(`Version reverted from ${tagVersion} to ${previousVersion}`);
console.log(
`Tag ${latestTag} has been deleted locally and remotely (if it existed).`,
);
console.log('Files have been updated. You may want to commit these changes.');
}
async function ensureCleanGit() {
const output = execSync('git status --porcelain', {
encoding: 'utf8',
}).trim();
if (output.length > 0) {
throw new Error(
'Working tree is not clean. Please commit or stash changes before undoing the version bump.',
);
}
}
async function getLatestVersionTag() {
const result = spawnSync(
'git',
['tag', '--sort=-version:refname', '--list', 'v*'],
{
encoding: 'utf8',
},
);
if (result.status !== 0) {
throw new Error('Failed to get git tags.');
}
const tags = result.stdout.trim().split('\n').filter(Boolean);
return tags[0] || null;
}
async function getTagCommit(tagName) {
const result = spawnSync('git', ['rev-parse', tagName], { encoding: 'utf8' });
if (result.status !== 0) {
throw new Error(`Failed to get commit for tag ${tagName}.`);
}
return result.stdout.trim();
}
async function getParentCommit(commitHash) {
const result = spawnSync('git', ['rev-parse', `${commitHash}^`], {
encoding: 'utf8',
});
if (result.status !== 0) {
return null;
}
return result.stdout.trim();
}
async function getVersionFromCommit(commitHash) {
const result = spawnSync('git', ['show', `${commitHash}:package.json`], {
encoding: 'utf8',
});
if (result.status !== 0) {
throw new Error(`Failed to read package.json from commit ${commitHash}.`);
}
const parsed = JSON.parse(result.stdout);
const version = parsed.version;
if (typeof version !== 'string') {
throw new Error(
'Root package.json from parent commit does not contain a valid version.',
);
}
assertValidSemver(version);
return version;
}
async function writeRootPackage(version) {
await updatePackageJSON(rootPackagePath, version);
}
async function writeClientPackage(version) {
await updatePackageJSON(clientPackagePath, version);
}
async function updatePackageJSON(filePath, version) {
const raw = await fs.readFile(filePath, 'utf8');
const parsed = JSON.parse(raw);
parsed.version = version;
const value = `${JSON.stringify(parsed, null, 2)}\n`;
await fs.writeFile(filePath, value, 'utf8');
}
async function writeEnv(version) {
let raw;
try {
raw = await fs.readFile(envPath, 'utf8');
} catch (error) {
if (error.code === 'ENOENT') {
raw = '';
} else {
throw error;
}
}
const lines = raw.split(/\r?\n/);
let replaced = false;
const nextLines = lines.map((line) => {
if (line.startsWith('VITE_CLIENT_VIEWER_VERSION=')) {
replaced = true;
return `VITE_CLIENT_VIEWER_VERSION=${version}`;
}
return line;
});
if (!replaced) {
nextLines.push(`VITE_CLIENT_VIEWER_VERSION=${version}`);
}
await fs.writeFile(envPath, `${nextLines.join('\n')}\n`, 'utf8');
}
function assertValidSemver(value) {
const semverPattern = /^(\d+)\.(\d+)\.(\d+)$/;
if (!semverPattern.test(value)) {
throw new Error(`Invalid semantic version: ${value}`);
}
}
async function deleteLocalTag(tagName) {
const result = spawnSync('git', [
'show-ref',
'--tags',
'--quiet',
`refs/tags/${tagName}`,
]);
if (result.status === 0) {
execSync(`git tag -d ${quote(tagName)}`, { stdio: 'inherit' });
console.log(`Deleted local tag: ${tagName}`);
} else {
console.log(`Local tag ${tagName} does not exist.`);
}
}
async function deleteRemoteTag(tagName) {
const result = spawnSync('git', ['ls-remote', '--tags', 'origin', tagName], {
encoding: 'utf8',
});
if (result.status === 0 && result.stdout.trim().length > 0) {
execSync(`git push origin :refs/tags/${quote(tagName)}`, {
stdio: 'inherit',
});
console.log(`Deleted remote tag: ${tagName}`);
} else {
console.log(`Remote tag ${tagName} does not exist.`);
}
}
function quote(value) {
return `'${value.replace(/'/g, "'\\''")}'`;
}
================================================
FILE: src/client-viewer/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
================================================
FILE: src/client-viewer/README.md
================================================
# Deskreen CE Client-Viewer
AGPL-3.0 License © [Pavlo (Paul) Buidenkov](https://github.com/pavlobu/deskreen)
================================================
FILE: src/client-viewer/index.html
================================================
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Deskreen CE Viewer</title>
<!-- GA tag ID will be loaded dynamically after user consent -->
<meta name="ga-tag-id" content="%VITE_CLIENT_VIEWER_GA_TAG%" />
<meta name="client-viewer-version" content="%VITE_CLIENT_VIEWER_VERSION%" />
<!-- GA request interceptor - blocks requests until user consent -->
<script>
(function() {
const CONSENT_KEY = 'deskreen_ga_consent';
const GA_DOMAINS = ['google-analytics.com', 'googletagmanager.com', 'google-analytics.co', 'analytics.google.com'];
for (let i = 1; i <= 20; i++) {
GA_DOMAINS.push('region' + i + '.google-analytics.com');
}
function getConsentStatus() {
try {
const stored = localStorage.getItem(CONSENT_KEY);
return stored === 'accepted' ? 'accepted' : null;
} catch {
return null;
}
}
function isGoogleAnalyticsUrl(url) {
try {
const urlObj = new URL(url, window.location.href);
const hostname = urlObj.hostname.toLowerCase();
return GA_DOMAINS.some(function(domain) {
return hostname === domain || hostname.endsWith('.' + domain);
});
} catch {
return false;
}
}
function shouldBlockRequest() {
return getConsentStatus() !== 'accepted';
}
function isLocalIP(ip) {
const parts = ip.split('.').map(Number);
if (parts.length !== 4 || parts.some(isNaN)) {
return false;
}
// 127.0.0.0/8
if (parts[0] === 127) return true;
// 10.0.0.0/8
if (parts[0] === 10) return true;
// 172.16.0.0/12
if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) return true;
// 192.168.0.0/16
if (parts[0] === 192 && parts[1] === 168) return true;
return false;
}
function sanitizeGAUrl(url) {
try {
const urlObj = new URL(url);
// only sanitize /g/collect requests
if (!urlObj.pathname.includes('/g/collect')) {
return url;
}
const dlParam = urlObj.searchParams.get('dl');
if (!dlParam) {
return url;
}
try {
const dlUrl = new URL(decodeURIComponent(dlParam));
const hostname = dlUrl.hostname;
if (isLocalIP(hostname)) {
urlObj.searchParams.set('dl', encodeURIComponent('http://localhost'));
return urlObj.toString();
}
} catch {
// if dl parameter is not a valid URL, leave it as is
}
return url;
} catch {
return url;
}
}
// intercept fetch
if (window.fetch) {
const originalFetch = window.fetch;
window.fetch = function(input, init) {
let url = typeof input === 'string' ? input : (input instanceof Request ? input.url : '');
if (isGoogleAnalyticsUrl(url)) {
if (shouldBlockRequest()) {
return Promise.reject(new Error('Google Analytics request blocked: user consent not granted'));
}
url = sanitizeGAUrl(url);
if (input instanceof Request) {
input = new Request(url, init || input);
} else {
input = url;
}
}
return originalFetch.apply(this, arguments);
};
}
// intercept XMLHttpRequest
if (window.XMLHttpRequest) {
const XHR = window.XMLHttpRequest;
const originalOpen = XHR.prototype.open;
const originalSend = XHR.prototype.send;
XHR.prototype.open = function(method, url, async, username, password) {
let urlString = typeof url === 'string' ? url : url.toString();
if (isGoogleAnalyticsUrl(urlString)) {
if (shouldBlockRequest()) {
throw new Error('Google Analytics request blocked: user consent not granted');
}
urlString = sanitizeGAUrl(urlString);
url = urlString;
}
this._interceptedUrl = urlString;
return originalOpen.call(this, method, url, async, username, password);
};
XHR.prototype.send = function() {
const url = this._interceptedUrl || '';
if (isGoogleAnalyticsUrl(url) && shouldBlockRequest()) {
return;
}
return originalSend.apply(this, arguments);
};
}
// intercept sendBeacon
if (navigator.sendBeacon) {
const originalSendBeacon = navigator.sendBeacon;
navigator.sendBeacon = function(url, data) {
let urlString = typeof url === 'string' ? url : url.toString();
if (isGoogleAnalyticsUrl(urlString)) {
if (shouldBlockRequest()) {
return false;
}
urlString = sanitizeGAUrl(urlString);
url = urlString;
}
return originalSendBeacon.call(this, url, data);
};
}
})();
</script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=%VITE_CLIENT_VIEWER_GA_TAG%"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){window.dataLayer.push(arguments);}
gtag('js', new Date());
// set default consent to denied (will be updated when user accepts)
gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied'
});
gtag('config', '%VITE_CLIENT_VIEWER_GA_TAG%');
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
================================================
FILE: src/client-viewer/package.json
================================================
{
"name": "deskreen-ce-client-viewer",
"private": true,
"version": "3.2.13",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@blueprintjs/core": "^6.3.4",
"i18next": "^25.6.2",
"i18next-http-backend": "^3.0.2",
"node-forge": "^1.3.1",
"normalize.css": "^8.0.1",
"pixelmatch": "^7.1.0",
"react": "^19.2.0",
"react-app-polyfill": "^3.0.0",
"react-dom": "^19.2.0",
"react-flexbox-grid": "^2.1.2",
"react-i18next": "^15.7.4",
"react-player": "^3.3.3",
"react-spinners": "^0.17.0",
"screenfull": "^6.0.2",
"shortid": "^2.2.17",
"simple-peer": "^9.11.1",
"socket.io-client": "^4.8.1",
"ua-parser-js": "^2.0.6",
"video.js": "^8.23.4"
},
"devDependencies": {
"@types/node-forge": "^1.3.14",
"@types/pixelmatch": "^5.2.6",
"@types/react": "^19.2.3",
"@types/react-dom": "^19.2.2",
"@types/resemblejs": "^4.1.3",
"@types/shortid": "^2.2.0",
"@types/socket.io-client": "^3.0.0",
"@types/ua-parser-js": "^0.7.39",
"@vitejs/plugin-legacy": "^7.2.1",
"@vitejs/plugin-react": "^5.1.0",
"typescript": "~5.9.3",
"vite": "^7.2.2",
"vite-plugin-node-polyfills": "^0.24.0"
}
}
================================================
FILE: src/client-viewer/public/img/.gitkeep
================================================
================================================
FILE: src/client-viewer/public/locales/da/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "Venter på at brugeren klikker TILLAD knappen på skærmdelingsenheden...",
"Waiting for user to select source to share from screen sharing device...": "Venter på at brugeren vælger kilden, som skal deles fra skærmdelingsenheden...",
"My Device Info": "Min enhedsinfo",
"Device Type": "Enhedstype",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "Din Enheds IP burde matche sammen med den Enheds IP, som ses i advarselspopup'en vist på din computer, hvor Deskreen-CE kører",
"Device IP": "Enhedens IP",
"Device Browser": "Enhedens Browser",
"Device OS": "Enhedens Operativsystem",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "Disse detaljer skal matche med dem, som du ser i advarselspopup'en på computerskærmen, hvor Deskreen-CE kører",
"Deskreen-CE Screen Viewer": "Deskreen-CE Skærmviser",
"Connected!": "Forbundet!",
"Error occurred": "Der skete en fejl",
"Deskreen-CE Error Dialog": "Deskreen-CE Fejl Dialog",
"Something went wrong": "Noget gik galt",
"You may close this browser window then try to connect again": "Prøv at lukke dette browservindue og forbind igen",
"An unknown error occurred": "Der opstod en ukendt fejl",
"You were not allowed to connect": "Der blev ikke tilladt forbindelse",
"You were disconnected": "Du blev afbrudt",
"WebRTC error occurred": "Der opstod en WebRTC fejl",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "Hvis du er vild med Deskreen-CE, så overvej at bidrage til Deskreen-CE financielt. Deskreen-CE er open-source. Dine donationer hjælper os med at forblive motiverede for at gøre Deskreen-CE endnu bedre.",
"Donate": "Donér",
"get-deskreen-pro": "Hent Deskreen Pro",
"get-deskreen-pro-tooltip": "Hent Deskreen Pro - åbner downloadsiden.",
"Video stream is paused": "Videostream er pauset",
"Video stream is playing": "Videostream kører",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "Videostream blev sat på pause efter at have forladt fuldskærmstilstand. Klik på Kør for at fortsætte.",
"Pause": "Pause",
"Play": "Kør",
"Video Settings": "Videoindstillinger",
"Flip": "Vend",
"Video quality has been changed to": "Videokvaliteten er blevet ændret til",
"Click to Open Video Settings": "Klik her for at åbne Videoindstillinger",
"Click to Enter Full Screen Mode": "Klik her for at gå ind i fuldskærmstilstand",
"Click to Play Video": "Klik for at afspille video",
"Click to Pause Video": "Klik for at pause video",
"Default video player has been turned OFF": "Standard videospiller er blevet slået FRA",
"Default video player has been turned ON": "Standard videospiller er blevet slået TIL",
"ON": "TIL",
"OFF": "FRA",
"Default Video Player": "Standard Videospiller",
"Click to visit our website": "Klik jer for at besøge vores hjemmeside",
"Video is flipped horizontally": "Videoen er vendt horisontalt",
"flip-the-screen-is-pro-version-only": "Vende skærmen er kun tilgængelig i Pro-versionen",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "Klik jer for at se forbindelsesinfo",
"Pair ID": "Par ID",
"Unpair": "Annullér Pardannelse",
"Session ID": "Sessionsid",
"Click to boost video stream if it is lagging": "Klik her for at booste videostreamen, hvis det lagger",
"Privacy Notice: Analytics in Deskreen CE Viewer": "Privatlivsmeddelelse: Analyse i Deskreen CE Viewer",
"Analytics Reference": "Analytik Reference",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "Denne app bruger Google Analytics (en gratis tjeneste fra Google) til anonymt at spore grundlæggende brugsdata. Det hjælper os med at forstå, hvordan appen bruges, så vi kan forbedre den for alle.",
"What we collect:": "Det vi indsamler:",
"Page views (which screens you visit)": "Sidevisninger (hvilke skærme du besøger)",
"Time spent on pages": "Tid brugt på sider",
"Basic device info (browser type, screen size)": "Grundlæggende enhedsinfo (browsertype, skærmstørrelse)",
"Your IP address (anonymized — last part removed for privacy)": "Din IP-adresse (anonymiseret — den sidste del fjernes af hensyn til privatlivet)",
"What we DON'T collect:": "Det vi IKKE indsamler:",
"Personal info (names, emails, passwords)": "Personlige oplysninger (navne, e-mails, adgangskoder)",
"Exact location": "Præcis placering",
"Any files or content you interact with": "Filer eller indhold, du interagerer med",
"Why anonymous?": "Hvorfor anonymt?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "Din IP bliver automatisk afkortet, og ingen kan identificere dig personligt ud fra disse data.",
"Your options:": "Dine muligheder:",
"Continue:": "Fortsæt:",
"We'll track anonymized usage to help improve the app.": "Vi registrerer anonymiseret brug for at hjælpe os med at forbedre appen.",
"Opt out:": "Fravælg:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Klik på knappen Afslå nedenfor for at deaktivere sporing. (Vi respekterer dette valg, men du kan gå glip af fremtidige forbedringer baseret på samlet feedback.)",
"Data goes to: Google Analytics. See their privacy policy.": "Data sendes til: Google Analytics. Se deres privatlivspolitik.",
"Accept": "Accepter",
"Allow": "Tillad",
"Deny": "Afslå",
"re-initiate-connection": "Genstart forbindelse",
"Privacy Settings": "Privatindstillinger",
"Change your preference:": "Ændre din præference:",
"Enable analytics:": "Aktivér analyse:",
"Disable analytics:": "Deaktivér analyse:",
"Enable Analytics": "Aktivér analyse",
"Disable Analytics": "Deaktivér analyse",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Klik på knappen Deaktivér nedenfor for at stoppe sporing. (Vi respekterer dette valg, men du kan gå glip af fremtidige forbedringer baseret på kollektiv feedback.)",
"their privacy policy": "deres privatlivspolitik"
}
================================================
FILE: src/client-viewer/public/locales/de/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "Warten bis der Nutzer auf dem Freigabegerät auf ZULASSEN klickt ...",
"Waiting for user to select source to share from screen sharing device...": "Warten bis der Nutzer eine Quelle für die Freigabe auswählt...",
"My Device Info": "Meine Geräteinformationen",
"Device Type": "Gerätetyp",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "Deine Geräte-IP sollte mit der \"Geräte-IP\" im Dialog auf dem Computer, auf dem Deskreen-CE läuft, übereinstimmen.",
"Device IP": "Geräte-IP",
"Device Browser": "Geräte-Browser",
"Device OS": "Geräte-Betriebssystem",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "Diese Informationen sollten mit denen im Dialog auf dem Freigabegerät übereinstimmen.",
"Deskreen-CE Screen Viewer": "Deskreen-CE Bildschrimansicht",
"Connected!": "Verbunden!",
"Error occurred": "Ein Fehler ist aufgetreten",
"Deskreen-CE Error Dialog": "Deskreen-CE Fehler Dialog",
"Something went wrong": "Etwas ist schief gegangen",
"You may close this browser window then try to connect again": "Schließe das Browserfenster und probiere es erneut",
"An unknown error occurred": "Ein unbekannter Fehler ist aufgetreten",
"You were not allowed to connect": "Die Verbindung wurde nicht zugelassen",
"You were disconnected": "Die Verbindung wurde getrennt",
"WebRTC error occurred": "WebRTC Fehler aufgetreten",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "Wenn dir Deskreen-CE gefällt, denke über eine Spende nach. Deskreen-CE ist Open-Source. Spenden motivieren uns, Deskreen-CE noch besser zu machen.",
"Donate": "Spenden",
"get-deskreen-pro": "Deskreen Pro erhalten",
"get-deskreen-pro-tooltip": "Deskreen Pro erhalten - öffnet die Download-Seite.",
"Video stream is paused": "Videostream ist pausiert",
"Video stream is playing": "Videostream läuft",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "Videostream wurde nach dem Verlassen des Vollbildmodus pausiert. Bitte klicken Sie auf Abspielen, um fortzufahren.",
"Pause": "Pause",
"Play": "Abspielen",
"Video Settings": "Video Einstellungen",
"Flip": "Drehen",
"Video quality has been changed to": "Videoqualität wurde geändert zu",
"Click to Open Video Settings": "Klicken um Videoeinstellungen zu öffnen",
"Click to Enter Full Screen Mode": "Klicken für Vollbild",
"Click to Play Video": "Klicken um Video abzuspielen",
"Click to Pause Video": "Klicken um Video anzuhalten",
"Default video player has been turned OFF": "Standard Video-Player wurde ausgeschaltet",
"Default video player has been turned ON": "Standard Video-Player wurde eingeschaltet",
"ON": "AN",
"OFF": "AUS",
"Default Video Player": "Standard Video-Player",
"Click to visit our website": "Klicken um unsere Website zu besuchen",
"Video is flipped horizontally": "Das Video ist horizontal gedreht",
"flip-the-screen-is-pro-version-only": "Bildschirm umdrehen ist nur in der Pro-Version verfügbar",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "Klicken um Verbindungsinformationen anzuzeigen",
"Pair ID": "Kopplungs-ID",
"Unpair": "Entkoppeln",
"Session ID": "Sitzungs-ID",
"Click to boost video stream if it is lagging": "Klicken um den Videostream zu verbessern, wenn er verzögert ist.",
"Privacy Notice: Analytics in Deskreen CE Viewer": "Datenschutzhinweis: Analyse in Deskreen CE Viewer",
"Analytics Reference": "Analytik-Referenz",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "Diese App verwendet Google Analytics (einen kostenlosen Dienst von Google), um anonyme Nutzungsdaten zu erfassen. So verstehen wir, wie die App genutzt wird, und können sie für alle verbessern.",
"What we collect:": "Was wir sammeln:",
"Page views (which screens you visit)": "Seitenaufrufe (welche Ansichten du besuchst)",
"Time spent on pages": "Verweildauer auf Seiten",
"Basic device info (browser type, screen size)": "Grundlegende Geräteinformationen (Browsertyp, Bildschirmgröße)",
"Your IP address (anonymized — last part removed for privacy)": "Deine IP-Adresse (anonymisiert – der letzte Teil wird aus Datenschutzgründen entfernt)",
"What we DON'T collect:": "Was wir NICHT sammeln:",
"Personal info (names, emails, passwords)": "Personenbezogene Daten (Namen, E-Mails, Passwörter)",
"Exact location": "Genauer Standort",
"Any files or content you interact with": "Dateien oder Inhalte, mit denen du interagierst",
"Why anonymous?": "Warum anonym?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "Deine IP wird automatisch gekürzt, sodass dich niemand anhand dieser Daten identifizieren kann.",
"Your options:": "Deine Optionen:",
"Continue:": "Weiter:",
"We'll track anonymized usage to help improve the app.": "Wir erfassen anonymisierte Nutzung, um die App zu verbessern.",
"Opt out:": "Ablehnen:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Klicke auf den Button Ablehnen unten, um das Tracking zu deaktivieren. (Wir respektieren diese Entscheidung, aber du könntest zukünftige Verbesserungen verpassen, die auf kollektivem Feedback basieren.)",
"Data goes to: Google Analytics. See their privacy policy.": "Datenempfänger: Google Analytics. Lies deren Datenschutzerklärung.",
"Accept": "Akzeptieren",
"Allow": "Erlauben",
"Deny": "Ablehnen",
"re-initiate-connection": "Verbindung erneut herstellen",
"Privacy Settings": "Datenschutzeinstellungen",
"Change your preference:": "Deine Präferenz ändern:",
"Enable analytics:": "Analytik aktivieren:",
"Disable analytics:": "Analytik deaktivieren:",
"Enable Analytics": "Analytik aktivieren",
"Disable Analytics": "Analytik deaktivieren",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Klicke auf den Button Deaktivieren unten, um das Tracking zu stoppen. (Wir respektieren diese Entscheidung, aber du könntest zukünftige Verbesserungen verpassen, die auf kollektivem Feedback basieren.)",
"their privacy policy": "deren Datenschutzerklärung"
}
================================================
FILE: src/client-viewer/public/locales/en/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "Waiting for video stream of screen sharing device...",
"Waiting for user to select source to share from screen sharing device...": "Waiting for user to select source to share from screen sharing device...",
"My Device Info": "My Device Info",
"Device Type": "Device Type",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "Your Device IP should match with \"Device IP\" in alert popup appeared on your computer, where Deskreen-CE is running.",
"Device IP": "Device IP",
"Device Browser": "Device Browser",
"Device OS": "Device OS",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "These details should match with the ones that you see in alert popup on screen sharing device.",
"Deskreen-CE Screen Viewer": "Deskreen-CE Screen Viewer",
"Connected!": "Connected!",
"Error occurred": "Error occurred",
"Deskreen-CE Error Dialog": "Deskreen-CE Error Dialog",
"Something went wrong": "Something went wrong",
"You may close this browser window then try to connect again": "You may close this browser window then try to connect again",
"An unknown error occurred": "An unknown error occurred",
"You were not allowed to connect": "You were not allowed to connect",
"You were disconnected": "You were disconnected",
"WebRTC error occurred": "WebRTC error occurred",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "If you like Deskreen-CE, consider contributing financially. Deskreen-CE is open-source. Your donations keep us motivated to make Deskreen-CE even better.",
"Donate": "Donate",
"get-deskreen-pro": "Get Deskreen Pro",
"get-deskreen-pro-tooltip": "Get Deskreen Pro - opens the download page.",
"Video stream is paused": "Video stream is paused",
"Video stream is playing": "Video stream is playing",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "Video stream paused after exiting fullscreen. Please click Play to continue.",
"Pause": "Pause",
"Play": "Play",
"Video Settings": "Video Settings",
"Flip": "Flip",
"Video quality has been changed to": "Video quality has been changed to",
"Click to Open Video Settings": "Click to Open Video Settings",
"Click to Enter Full Screen Mode": "Click to Enter Full Screen Mode",
"Click to Play Video": "Click to Play Video",
"Click to Pause Video": "Click to Pause Video",
"Default video player has been turned OFF": "Default video player has been turned OFF",
"Default video player has been turned ON": "Default video player has been turned ON",
"ON": "ON",
"OFF": "OFF",
"Default Video Player": "Default Video Player",
"Click to visit our website": "Click to visit our website",
"Video is flipped horizontally": "Video is flipped horizontally",
"flip-the-screen-is-pro-version-only": "Flip the screen is pro version only",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "Click to see connection info",
"Pair ID": "Pair ID",
"Unpair": "Unpair",
"Session ID": "Session ID",
"Click to boost video stream if it is lagging": "Click to boost video stream if it is lagging",
"re-initiate-connection": "Re-initiate Connection",
"Privacy Notice: Analytics in Deskreen CE Viewer": "Privacy Notice: Analytics in Deskreen CE Viewer",
"Analytics Reference": "Analytics Reference",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.",
"What we collect:": "What we collect:",
"Page views (which screens you visit)": "Page views (which screens you visit)",
"Time spent on pages": "Time spent on pages",
"Basic device info (browser type, screen size)": "Basic device info (browser type, screen size)",
"Your IP address (anonymized — last part removed for privacy)": "Your IP address (anonymized — last part removed for privacy)",
"What we DON'T collect:": "What we DON'T collect:",
"Personal info (names, emails, passwords)": "Personal info (names, emails, passwords)",
"Exact location": "Exact location",
"Any files or content you interact with": "Any files or content you interact with",
"Why anonymous?": "Why anonymous?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "Your IP is automatically shortened, and no one can identify you personally from this data.",
"Your options:": "Your options:",
"Continue:": "Continue:",
"We'll track anonymized usage to help improve the app.": "We'll track anonymized usage to help improve the app.",
"Opt out:": "Opt out:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)",
"Data goes to: Google Analytics. See their privacy policy.": "Data goes to: Google Analytics. See their privacy policy.",
"Accept": "Accept",
"Allow": "Allow",
"Deny": "Deny",
"Privacy Settings": "Privacy Settings",
"Change your preference:": "Change your preference:",
"Enable analytics:": "Enable analytics:",
"Disable analytics:": "Disable analytics:",
"Enable Analytics": "Enable Analytics",
"Disable Analytics": "Disable Analytics",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)",
"their privacy policy": "their privacy policy"
}
================================================
FILE: src/client-viewer/public/locales/es/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "Esperando que el usuario haga clic en el botón PERMITIR en el dispositivo para compartir pantalla ...",
"Waiting for user to select source to share from screen sharing device...": "Esperando que el usuario seleccione la fuente para compartir desde el dispositivo para compartir pantalla ...",
"My Device Info": "Información de mi dispositivo",
"Device Type": "Tipo del dispositivo",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "La IP de tu dispositivo debe coincidir con \"IP del dispositivo \" en la ventana emergente de alerta que apareció en la computadora donde se está ejecutando Deskreen-CE.",
"Device IP": "IP del dispositivo",
"Device Browser": "Navegador del dispositivo",
"Device OS": "SO del dispositivo",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "Estos detalles deben coincidir con los que ves en la ventana emergente en el dispositivo para compartir pantalla.",
"Deskreen-CE Screen Viewer": "Visor de pantalla de Deskreen-CE",
"Connected!": "¡Conectado!",
"Error occurred": "Ocurrió un error",
"Deskreen-CE Error Dialog": "Cuadro de diálogo de error de Deskreen-CE",
"Something went wrong": "Algo salió mal",
"You may close this browser window then try to connect again": "Puedes cerrar esta ventana del navegador y luego intentar conectarte nuevamente",
"An unknown error occurred": "Ocurrió un error desconocido",
"You were not allowed to connect": "No se te permitió conectarte",
"You were disconnected": "Fuiste desconectado",
"WebRTC error occurred": "Ocurrió un error de WebRTC",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "Si te gusta Deskreen-CE, considera la posibilidad de contribuir económicamente. Deskreen-CE es de código abierto. Tus donaciones nos mantienen motivados para hacer que Deskreen-CE sea aún mejor.",
"Donate": "Donar",
"get-deskreen-pro": "Obtener Deskreen Pro",
"get-deskreen-pro-tooltip": "Obtener Deskreen Pro - abre la página de descarga.",
"Video stream is paused": "La transmisión de video está en pausa",
"Video stream is playing": "La transmisión de video está en reproducción",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "La transmisión de video se pausó después de salir de pantalla completa. Por favor, haz clic en Reproducir para continuar.",
"Pause": "Pausa",
"Play": "Reproducir",
"Video Settings": "Configuraciones de video",
"Flip": "Voltear",
"Video quality has been changed to": "La calidad de video se ha cambiado a",
"Click to Open Video Settings": "Clic para abrir las configuraciones de video",
"Click to Enter Full Screen Mode": "Clic para entrar en el modo de pantalla completa",
"Click to Play Video": "Clic para reproducir el video",
"Click to Pause Video": "Clic para pausar el video",
"Default video player has been turned OFF": "El reproductor de video predeterminado se ha APAGADO",
"Default video player has been turned ON": "El reproductor de video predeterminado se ha ENCENDIDO",
"ON": "ENCENDER",
"OFF": "APAGAR",
"Default Video Player": "Reproductor de video predeterminado",
"Click to visit our website": "Clic para visitar nuestro sitio web",
"Video is flipped horizontally": "El video se ha volteado horizontalmente",
"flip-the-screen-is-pro-version-only": "Voltear la pantalla está disponible solo en la versión Pro",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "Clic para ver la información de la conexión",
"Pair ID": "ID del par",
"Unpair": "Desemparejar",
"Session ID": "ID de sesión",
"Click to boost video stream if it is lagging": "Haz clic para mejorar la transmisión de video si se está retrasando",
"Privacy Notice: Analytics in Deskreen CE Viewer": "Aviso de privacidad: Analítica en Deskreen CE Viewer",
"Analytics Reference": "Referencia de Analítica",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "Esta aplicación utiliza Google Analytics (un servicio gratuito de Google) para registrar de manera anónima datos básicos de uso. Esto nos ayuda a entender cómo se usa la aplicación para poder mejorarla para todos.",
"What we collect:": "Lo que recopilamos:",
"Page views (which screens you visit)": "Vistas de página (qué pantallas visitas)",
"Time spent on pages": "Tiempo invertido en las páginas",
"Basic device info (browser type, screen size)": "Información básica del dispositivo (tipo de navegador, tamaño de pantalla)",
"Your IP address (anonymized — last part removed for privacy)": "Tu dirección IP (anonimizada: se elimina la última parte por privacidad)",
"What we DON'T collect:": "Lo que NO recopilamos:",
"Personal info (names, emails, passwords)": "Información personal (nombres, correos electrónicos, contraseñas)",
"Exact location": "Ubicación exacta",
"Any files or content you interact with": "Cualquier archivo o contenido con el que interactúes",
"Why anonymous?": "¿Por qué anónimo?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "Tu IP se acorta automáticamente, y nadie puede identificarte personalmente con estos datos.",
"Your options:": "Tus opciones:",
"Continue:": "Continuar:",
"We'll track anonymized usage to help improve the app.": "Registraremos uso anonimizado para ayudar a mejorar la aplicación.",
"Opt out:": "Rechazar:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Haz clic en el botón Rechazar a continuación para desactivar el seguimiento. (Respetaremos esta elección, pero podrías perderte mejoras futuras basadas en comentarios colectivos).",
"Data goes to: Google Analytics. See their privacy policy.": "Los datos van a: Google Analytics. Consulta su política de privacidad.",
"Accept": "Aceptar",
"Allow": "Permitir",
"Deny": "Rechazar",
"re-initiate-connection": "Restablecer conexión",
"Privacy Settings": "Configuración de privacidad",
"Change your preference:": "Cambiar tu preferencia:",
"Enable analytics:": "Habilitar analítica:",
"Disable analytics:": "Deshabilitar analítica:",
"Enable Analytics": "Habilitar Analítica",
"Disable Analytics": "Deshabilitar Analítica",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Haz clic en el botón Deshabilitar a continuación para detener el seguimiento. (Respetaremos esta elección, pero podrías perderte mejoras futuras basadas en comentarios colectivos).",
"their privacy policy": "su política de privacidad"
}
================================================
FILE: src/client-viewer/public/locales/fi/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "Odotetaan että käyttäjä napsauttaa SALLI-painiketta ruudunjakolaitteessa...",
"Waiting for user to select source to share from screen sharing device...": "Odotetaan että käyttäjä valitsee ruudunjakolaitteesta lähteen joka jaetaan...",
"My Device Info": "Tiedot laitteestani",
"Device Type": "Laitteen malli",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "Laitteesi IP:n tulisi täsmätä \"Laitteen IP\" kohdassa joka näkyy ilmoiteikkunassa tietokoneella jossa Deskreen-CE on käynnissä.",
"Device IP": "Laitteen IP",
"Device Browser": "Laiteselain",
"Device OS": "Laitteen käyttöjärjestelmä",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "Näiden yksityiskohtien tulisi täsmätä niiden kanssa jotka näet ruudunjakolaitteen ilmoitekehotteessa, Deskreen-CE:in ollessa käynnissä",
"Deskreen-CE Screen Viewer": "Deskreen-CE-ruutukatselin",
"Connected!": "Yhdistetty!",
"Error occurred": "Tapahtui virhe",
"Deskreen-CE Error Dialog": "Deskreen-CE:in virhekooste",
"Something went wrong": "Jokin meni pieleen",
"You may close this browser window then try to connect again": "Voit sulkea tämän selainikkunan koettaaksesi uudelleenyhdistämistä",
"An unknown error occurred": "Ilmeni tuntematon virhe",
"You were not allowed to connect": "Yhdistämistä ei sallittu",
"You were disconnected": "Sinulta katkesi yhteys",
"WebRTC error occurred": "Ilmeni WebRTC-virhe",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "Mikäli pidät Deskreen-CE:istä, harkitsethan rahallista lahjoitusta. Deskreen-CE on avoimen lähdekoodin ohjelma. Lahjoituksesi auttavat motivaatiomme säilymisen kannalta tehdäksemme Deskreen-CE:istä vieläkin paremman.",
"Donate": "Lahjoita",
"get-deskreen-pro": "Hanki Deskreen Pro",
"get-deskreen-pro-tooltip": "Hanki Deskreen Pro - avaa lataussivun.",
"Video stream is paused": "Videolähetys on tauolla",
"Video stream is playing": "Videolähetys on käynnissä",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "Videolähetys pausattiin kokoruututilasta poistumisen jälkeen. Napsauta Toista jatkaaksesi.",
"Pause": "Tauko",
"Play": "Toista",
"Video Settings": "Asetukset videolle",
"Flip": "Käännä ympäri",
"Video quality has been changed to": "Videon laatu muutettiin määreeseen",
"Click to Open Video Settings": "Napsauta avataksesi videon asetukset",
"Click to Enter Full Screen Mode": "Napsauta siirtyäksesi kokoruututilaan",
"Click to Play Video": "Napsauta toistaaksesi videon",
"Click to Pause Video": "Napsauta pysäyttääksesi videon",
"Default video player has been turned OFF": "Vakiollinen videotoisto-ohjelma on KYTKETTY POIS PÄÄLTÄ",
"Default video player has been turned ON": "Vakiollinen videotoisto-ohjelma on KYTKETTY PÄÄLLE",
"ON": "PÄÄLLÄ",
"OFF": "POIS",
"Default Video Player": "Vakiollinen videontoisto-ohjelma",
"Click to visit our website": "Napsauta vieraillaksesi verkkosivustollamme",
"Video is flipped horizontally": "Video käännetty vaakatasossa",
"flip-the-screen-is-pro-version-only": "Näytön kääntäminen on saatavilla vain Pro-versiossa",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "Napsauta katsoaksesi tietoja yhteydestäsi",
"Pair ID": "Lateparin ID-tunniste",
"Unpair": "Poista laiteparitus",
"Session ID": "Istunnon ID-tunniste",
"Click to boost video stream if it is lagging": "Napsauta lisätyöntöapua videovirtaukselle mikäli se hidastelee",
"Privacy Notice: Analytics in Deskreen CE Viewer": "Tietosuojailmoitus: Analytiikka Deskreen CE Viewer -sovelluksessa",
"Analytics Reference": "Analytiikan viite",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "Tämä sovellus käyttää Google Analyticsia (Googlelta saatava ilmainen palvelu) seuratakseen nimettömästi perustason käyttötietoja. Se auttaa meitä ymmärtämään, miten sovellusta käytetään, jotta voimme parantaa sitä kaikille.",
"What we collect:": "Mitä keräämme:",
"Page views (which screens you visit)": "Sivunäyttökerrat (mitä näkymiä käyt)",
"Time spent on pages": "Sivuille käytetty aika",
"Basic device info (browser type, screen size)": "Laitteen perustiedot (selaintyyppi, näytön koko)",
"Your IP address (anonymized — last part removed for privacy)": "IP-osoitteesi (anonymisoitu — viimeinen osa poistetaan yksityisyyden suojaamiseksi)",
"What we DON'T collect:": "Mitä emme kerää:",
"Personal info (names, emails, passwords)": "Henkilötietoja (nimiä, sähköposteja, salasanoja)",
"Exact location": "Tarkkaa sijaintia",
"Any files or content you interact with": "Tiedostoja tai sisältöä, joiden kanssa olet vuorovaikutuksessa",
"Why anonymous?": "Miksi anonyymisti?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "IP-osoitteesi lyhennetään automaattisesti, eikä sinua voi tunnistaa näiden tietojen perusteella.",
"Your options:": "Vaihtoehtosi:",
"Continue:": "Jatka:",
"We'll track anonymized usage to help improve the app.": "Seuraamme anonymisoitua käyttöä sovelluksen parantamiseksi.",
"Opt out:": "Kieltäydy:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Napsauta Hylkää-painiketta alla poistaaksesi seurannan käytöstä. (Kunnioitamme tätä valintaa, mutta saatat jäädä paitsi tulevista parannuksista, jotka perustuvat yhteiseen palautteeseen.)",
"Data goes to: Google Analytics. See their privacy policy.": "Tiedot lähetetään: Google Analytics. Tutustu heidän tietosuojakäytäntöönsä.",
"Accept": "Hyväksy",
"Allow": "Salli",
"Deny": "Hylkää",
"re-initiate-connection": "Käynnistä yhteys uudelleen",
"Privacy Settings": "Tietosuoja-asetukset",
"Change your preference:": "Muuta mieltymystäsi:",
"Enable analytics:": "Ota analytiikka käyttöön:",
"Disable analytics:": "Poista analytiikka käytöstä:",
"Enable Analytics": "Ota analytiikka käyttöön",
"Disable Analytics": "Poista analytiikka käytöstä",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Klikkaa alla olevaa Poista käytöstä -painiketta seurannan lopettamiseksi. (Kunnioitamme tätä valintaa, mutta saatat jäädä paitsi tulevista parannuksista, jotka perustuvat kollektiiviseen palautteeseen.)",
"their privacy policy": "heidän tietosuojakäytäntönsä"
}
================================================
FILE: src/client-viewer/public/locales/fr/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "En attente de la validation depuis l'appareil source...",
"Waiting for user to select source to share from screen sharing device...": "En attente de la sélection de la source à partager depuis l'appareil source...",
"My Device Info": "Mes informations d'appareil",
"Device Type": "Type d'appareil",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "Votre adresse IP doit correspondre avec l'\"Adresse IP\" affiché dans la pop-up affichée sur l'ordinateur depuis lequel Deskreen-CE est lancé.",
"Device IP": "IP de l'appareil",
"Device Browser": "Navigateur de l'appareil",
"Device OS": "OS de l'appareil",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "Ces détails doivent correspondre avec ceux inscrits dans la pop-up affichée sur l'ordinateur depuis lequel Deskreen-CE est lancé..",
"Deskreen-CE Screen Viewer": "Écran de visionnage Deskreen-CE",
"Connected!": "Connecté!",
"Error occurred": "Une erreur est survenue",
"Deskreen-CE Error Dialog": "Boîte de dialogue d'erreur",
"Something went wrong": "Quelque chose s'est mal passé",
"You may close this browser window then try to connect again": "Vous devriez fermer cette fenêtre de navigateur et essayer de vous connecter de nouveau",
"An unknown error occurred": "Une erreur inconnue s'est produite",
"You were not allowed to connect": "Vous n'êtes pas autorisé à vous connecter",
"You were disconnected": "Vous avez été déconnecté",
"WebRTC error occurred": "Une erreur WebRTC s'est produite",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "Si vous aimez Deskreen-CE, Vous pouvez contribuer financièrement. Deskreen-CE est open-source. Votre don nous motivera à rendre Deskreen-CE encore meilleur.",
"Donate": "Donner",
"get-deskreen-pro": "Obtenir Deskreen Pro",
"get-deskreen-pro-tooltip": "Obtenir Deskreen Pro - ouvre la page de téléchargement.",
"Video stream is paused": "Le flux vidéo est en pause",
"Video stream is playing": "Lecture du flux vidéo",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "Le flux vidéo est en pause après avoir quitté le mode plein écran. Veuillez cliquer sur Lecture pour continuer.",
"Pause": "Pause",
"Play": "Lecture",
"Video Settings": "Paramètres Vidéo",
"Flip": "Tourner",
"Video quality has been changed to": "Qualité de la vidéo changée en",
"Click to Open Video Settings": "Cliquez pour ouvrir les paramètres vidéo",
"Click to Enter Full Screen Mode": "Cliquez pour passer en plein écran",
"Click to Play Video": "Cliquez pour lire la vidéo",
"Click to Pause Video": "Cliquez pour mettre en pause la vidéo",
"Default video player has been turned OFF": "Le lecteur vidéo par défaut a été désactivé",
"Default video player has been turned ON": "Le lecteur vidéo par défaut a été activé",
"ON": "ON",
"OFF": "OFF",
"Default Video Player": "Lecteur vidéo par défaut",
"Click to visit our website": "Cliquez ici pour visiter notre site web",
"Video is flipped horizontally": "La vidéo à été tourner horizontallement",
"flip-the-screen-is-pro-version-only": "Retourner l'écran n'est disponible que dans la version Pro",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "Cliquez pour voir les informations de connexion",
"Pair ID": "ID d'appairage",
"Unpair": "Desappairer",
"Session ID": "ID de session",
"Click to boost video stream if it is lagging": "Cliquez pour booster le flux vidéo si vous rencontrez des ralentissements",
"Privacy Notice: Analytics in Deskreen CE Viewer": "Avis de confidentialité : Analyses dans Deskreen CE Viewer",
"Analytics Reference": "Référence Analytique",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "Cette application utilise Google Analytics (un service gratuit de Google) pour suivre anonymement des données d'utilisation de base. Cela nous aide à comprendre comment l'application est utilisée afin de l'améliorer pour tout le monde.",
"What we collect:": "Ce que nous recueillons :",
"Page views (which screens you visit)": "Pages consultées (les écrans que vous visitez)",
"Time spent on pages": "Temps passé sur les pages",
"Basic device info (browser type, screen size)": "Informations de base sur l'appareil (type de navigateur, taille de l'écran)",
"Your IP address (anonymized — last part removed for privacy)": "Votre adresse IP (anonymisée — la dernière partie est supprimée pour protéger votre vie privée)",
"What we DON'T collect:": "Ce que nous NE collectons PAS :",
"Personal info (names, emails, passwords)": "Informations personnelles (noms, adresses e-mail, mots de passe)",
"Exact location": "Localisation précise",
"Any files or content you interact with": "Les fichiers ou contenus avec lesquels vous interagissez",
"Why anonymous?": "Pourquoi anonyme ?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "Votre adresse IP est automatiquement raccourcie, et personne ne peut vous identifier personnellement à partir de ces données.",
"Your options:": "Vos options :",
"Continue:": "Continuer :",
"We'll track anonymized usage to help improve the app.": "Nous suivrons l'utilisation anonymisée pour aider à améliorer l'application.",
"Opt out:": "Refuser :",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Cliquez sur le bouton Refuser ci-dessous pour désactiver le suivi. (Nous respecterons ce choix, mais vous pourriez manquer des améliorations futures basées sur les retours collectifs.)",
"Data goes to: Google Analytics. See their privacy policy.": "Les données sont envoyées à : Google Analytics. Consultez leur politique de confidentialité.",
"Accept": "Accepter",
"Allow": "Autoriser",
"Deny": "Refuser",
"re-initiate-connection": "Réinitialiser la connexion",
"Privacy Settings": "Paramètres de confidentialité",
"Change your preference:": "Modifier votre préférence :",
"Enable analytics:": "Activer les analyses :",
"Disable analytics:": "Désactiver les analyses :",
"Enable Analytics": "Activer les analyses",
"Disable Analytics": "Désactiver les analyses",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Cliquez sur le bouton Désactiver ci-dessous pour arrêter le suivi. (Nous respecterons ce choix, mais vous pourriez manquer des améliorations futures basées sur les retours collectifs.)",
"their privacy policy": "leur politique de confidentialité"
}
================================================
FILE: src/client-viewer/public/locales/it/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "In attesa che l'utente faccia clic sul pulsante CONSENTI sul dispositivo di condivisione...",
"Waiting for user to select source to share from screen sharing device...": "In attesa che l'utente selezioni la sorgente da condividere dal dispositivo di condivisione...",
"My Device Info": "Info del mio Dispositivo",
"Device Type": "Tipologia Dispositivo",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "L'IP del tuo Dispositivo dovrebbe corrispondere a \"IP Dispositivo\" nel popup apparso sul tuo computer, dove Deskreen-CE è in esecuzione.",
"Device IP": "IP Dispositivo",
"Device Browser": "Browser Dispositivo",
"Device OS": "OS Dispositivo",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "Questi dettagli dovrebbero corrispondere a quelli che vedi nel popup sul Dispositivo di condivisione.",
"Deskreen-CE Screen Viewer": "Visualizzatore dello schermo di Deskreen-CE",
"Connected!": "Connesso!",
"Error occurred": "Si è verificato un Errore",
"Deskreen-CE Error Dialog": "Finestra di dialogo degli errori di Deskreen-CE",
"Something went wrong": "Qualcosa è andato storto",
"You may close this browser window then try to connect again": "Puoi chiudere questa finestra del browser, quindi provare a connetterti di nuovo",
"An unknown error occurred": "Si è verificato un errore sconosciuto",
"You were not allowed to connect": "Non ti è stato permesso di connetterti",
"You were disconnected": "Sei stato disconnesso",
"WebRTC error occurred": "Si è verificato un errore WebRTC",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "Se ti piace Deskreen-CE, considera di contribuire finanziariamente. Deskreen-CE è open-source. Le tue donazioni ci motivano a rendere Deskreen-CE ancora migliore.",
"Donate": "Dona",
"get-deskreen-pro": "Ottieni Deskreen Pro",
"get-deskreen-pro-tooltip": "Ottieni Deskreen Pro - apre la pagina di download.",
"Video stream is paused": "Trasmissione Video in pausa",
"Video stream is playing": "Trasmissione Video in riproduzione",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "Trasmissione video in pausa dopo l'uscita dalla modalità schermo intero. Clicca su Riproduci per continuare.",
"Pause": "Pausa",
"Play": "Riproduci",
"Video Settings": "Impostazioni Video",
"Flip": "Capovolgi",
"Video quality has been changed to": "La qualità Video è stata cambiata a",
"Click to Open Video Settings": "Clicca per aprire le Impostazioni Video",
"Click to Enter Full Screen Mode": "Clicca per entrare in modalità Schermo Intero",
"Click to Play Video": "Clicca per riprodurre il video",
"Click to Pause Video": "Clicca per mettere in pausa il video",
"Default video player has been turned OFF": "il player video predefinito è stato spento",
"Default video player has been turned ON": "il player video predefinito è stato acceso",
"ON": "Acceso",
"OFF": "Spento",
"Default Video Player": "Player Video Predefinito",
"Click to visit our website": "Clicca per visitare il nostro sito",
"Video is flipped horizontally": "Il Video è capovolto orizzontalmente",
"flip-the-screen-is-pro-version-only": "Capovolgere lo schermo è disponibile solo nella versione Pro",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "Clicca per vedere le info di connessione",
"Pair ID": "ID Coppia",
"Unpair": "Disaccoppia",
"Session ID": "ID Sessione",
"Click to boost video stream if it is lagging": "Clicca per incrementare il flusso video se sta andando a scatti",
"Privacy Notice: Analytics in Deskreen CE Viewer": "Informativa sulla privacy: Analisi in Deskreen CE Viewer",
"Analytics Reference": "Riferimento Analitico",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "Questa app utilizza Google Analytics (un servizio gratuito di Google) per tracciare in modo anonimo i dati di utilizzo di base. Questo ci aiuta a capire come viene usata l'app, così possiamo migliorarla per tutti.",
"What we collect:": "Cosa raccogliamo:",
"Page views (which screens you visit)": "Visualizzazioni di pagina (quali schermate visiti)",
"Time spent on pages": "Tempo trascorso sulle pagine",
"Basic device info (browser type, screen size)": "Informazioni di base sul dispositivo (tipo di browser, dimensioni dello schermo)",
"Your IP address (anonymized — last part removed for privacy)": "Il tuo indirizzo IP (anonimizzato — l'ultima parte viene rimossa per la privacy)",
"What we DON'T collect:": "Cosa NON raccogliamo:",
"Personal info (names, emails, passwords)": "Dati personali (nomi, email, password)",
"Exact location": "Posizione esatta",
"Any files or content you interact with": "Qualsiasi file o contenuto con cui interagisci",
"Why anonymous?": "Perché anonimo?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "Il tuo IP viene accorciato automaticamente e nessuno può identificarti personalmente da questi dati.",
"Your options:": "Le tue opzioni:",
"Continue:": "Continua:",
"We'll track anonymized usage to help improve the app.": "Tracceremo l'utilizzo anonimizzato per aiutare a migliorare l'app.",
"Opt out:": "Rifiuta:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Fai clic sul pulsante Rifiuta qui sotto per disattivare il tracciamento. (Rispetteremo questa scelta, ma potresti perdere miglioramenti futuri basati sul feedback collettivo.)",
"Data goes to: Google Analytics. See their privacy policy.": "I dati vengono inviati a: Google Analytics. Consulta la loro informativa sulla privacy.",
"Accept": "Accetta",
"Allow": "Consenti",
"Deny": "Rifiuta",
"re-initiate-connection": "Riavvia la connessione",
"Privacy Settings": "Impostazioni privacy",
"Change your preference:": "Modifica la tua preferenza:",
"Enable analytics:": "Abilita analisi:",
"Disable analytics:": "Disabilita analisi:",
"Enable Analytics": "Abilita analisi",
"Disable Analytics": "Disabilita analisi",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Fai clic sul pulsante Disabilita qui sotto per interrompere il tracciamento. (Rispetteremo questa scelta, ma potresti perdere miglioramenti futuri basati su feedback collettivo.)",
"their privacy policy": "la loro informativa sulla privacy"
}
================================================
FILE: src/client-viewer/public/locales/ja/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "画面共有デバイスでユーザーが「許可」をクリックするのを待っています...",
"Waiting for user to select source to share from screen sharing device...": "画面共有デバイスから共有するソースをユーザーが選択するのを待っています...",
"My Device Info": "このデバイスの情報",
"Device Type": "デバイスの種類",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "Deskreen-CEが動作しているパソコンに表示されるアラートポップアップの\"デバイスIP\"と、このデバイスのデバイスIPが一致する必要があります。",
"Device IP": "デバイスのIP",
"Device Browser": "デバイスのブラウザ",
"Device OS": "デバイスのOS",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "これらの内容は、画面共有デバイスのアラートポップアップに表示される内容と一致している必要があります。",
"Deskreen-CE Screen Viewer": "Deskreen-CE Screen Viewer",
"Connected!": "接続されました!",
"Error occurred": "エラーが発生しました",
"Deskreen-CE Error Dialog": "Deskreen-CE エラーダイアログ",
"Something went wrong": "何らかの問題が発生しました",
"You may close this browser window then try to connect again": "このブラウザを閉じてから、再度接続を試みてください",
"An unknown error occurred": "不明なエラーが発生しました",
"You were not allowed to connect": "接続が許可されていません",
"You were disconnected": "接続が切断されました",
"WebRTC error occurred": "WebRTCエラーが発生しました",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "Deskreen-CEを気に入っていただけたなら、資金面での貢献をご検討ください。Deskreen-CEはオープンソースです。あなたの寄付により、私たちはDeskreen-CEをより良いものにするためのモチベーションを保つことができます。",
"Donate": "寄付",
"get-deskreen-pro": "Deskreen Pro を入手",
"get-deskreen-pro-tooltip": "Deskreen Pro を入手 - ダウンロードページを開きます。",
"Video stream is paused": "ビデオストリームを一時停止しています",
"Video stream is playing": "ビデオストリームを再生中です",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "フルスクリーンモードを終了した後、ビデオストリームが一時停止されました。続けるには再生をクリックしてください。",
"Pause": "一時停止",
"Play": "再生",
"Video Settings": "ビデオ設定",
"Flip": "反転",
"Video quality has been changed to": "ビデオの画質を変更しました。画質:",
"Click to Open Video Settings": "クリックしてビデオ設定を開きます",
"Click to Enter Full Screen Mode": "クリックするとフルスクリーンモードになります",
"Click to Play Video": "クリックしてビデオを再生します",
"Click to Pause Video": "クリックしてビデオを一時停止します",
"Default video player has been turned OFF": "デフォルトのビデオプレーヤーがOFFになっています",
"Default video player has been turned ON": "デフォルトのビデオプレーヤーがONになっています",
"ON": "ON",
"OFF": "OFF",
"Default Video Player": "デフォルトのビデオプレーヤー",
"Click to visit our website": "クリックするとウェブサイトが開きます",
"Video is flipped horizontally": "映像が水平方向に反転しています",
"flip-the-screen-is-pro-version-only": "画面の反転はPro版でのみ利用可能です",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "クリックすると接続情報が表示されます",
"Pair ID": "ペアID",
"Unpair": "ペア解除",
"Session ID": "セッションID",
"Click to boost video stream if it is lagging": "クリックすると、ビデオストリームが遅延している場合、ブーストされます",
"Privacy Notice: Analytics in Deskreen CE Viewer": "プライバシーに関するお知らせ:Deskreen CE Viewerでの解析について",
"Analytics Reference": "分析参照",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "このアプリは Google が提供する無料サービスの Google Analytics を使用して、基本的な利用データを匿名で追跡します。アプリの使われ方を理解し、すべてのユーザーのために改善するためです。",
"What we collect:": "収集するデータ:",
"Page views (which screens you visit)": "ページビュー(どの画面を表示したか)",
"Time spent on pages": "ページに滞在した時間",
"Basic device info (browser type, screen size)": "基本的な端末情報(ブラウザーの種類、画面サイズ)",
"Your IP address (anonymized — last part removed for privacy)": "IP アドレス(匿名化 — プライバシー保護のため末尾を削除)",
"What we DON'T collect:": "収集しないもの:",
"Personal info (names, emails, passwords)": "個人情報(氏名、メールアドレス、パスワード)",
"Exact location": "正確な位置情報",
"Any files or content you interact with": "操作したファイルやコンテンツ",
"Why anonymous?": "なぜ匿名なのですか?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "IP アドレスは自動的に短縮され、このデータから個人を特定することはできません。",
"Your options:": "選択肢:",
"Continue:": "続行:",
"We'll track anonymized usage to help improve the app.": "アプリ改善のために匿名化された利用状況を追跡します。",
"Opt out:": "オプトアウト:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "下の「拒否」ボタンをクリックすると追跡を無効にできます。(この選択は尊重しますが、総合的なフィードバックに基づく将来の改善を逃す可能性があります。)",
"Data goes to: Google Analytics. See their privacy policy.": "データ送信先:Google Analytics。プライバシーポリシーをご確認ください。",
"Accept": "同意する",
"Allow": "許可する",
"Deny": "拒否",
"re-initiate-connection": "再接続",
"Privacy Settings": "プライバシー設定",
"Change your preference:": "設定を変更:",
"Enable analytics:": "分析を有効にする:",
"Disable analytics:": "分析を無効にする:",
"Enable Analytics": "分析を有効にする",
"Disable Analytics": "分析を無効にする",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "下の無効ボタンをクリックして追跡を停止します。(この選択を尊重しますが、集団フィードバックに基づく将来の改善を見逃す可能性があります。)",
"their privacy policy": "プライバシーポリシー"
}
================================================
FILE: src/client-viewer/public/locales/ko/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "공유할 기기의 사용자가 화면 공유 허용 버튼을 클릭하기를 기다리는 중 ...",
"Waiting for user to select source to share from screen sharing device...": "공유할 기기의 어떤 화면을 공유할지 선택을 기다리는 중...",
"My Device Info": "내 기기 정보",
"Device Type": "기기 종류",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "현재 기기의 IP는 Deskreen-CE 이 제공하는 \"Device IP\" 와 같아야 합니다.",
"Device IP": "기기 IP",
"Device Browser": "기기 브라우저",
"Device OS": "기기 OS",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "세부 사항은 화면 공유 장치에서 팝업에서 표시되는 것과 일치해야합니다.",
"Deskreen-CE Screen Viewer": "스크린 뷰어",
"Connected!": "연결되었습니다.",
"Error occurred": "오류가 발생했습니다",
"Deskreen-CE Error Dialog": "오류 알림",
"Something went wrong": "연결과정에 오류가 발생하였습니다",
"You may close this browser window then try to connect again": "이 브라우저 창을 닫은 다음 다시 연결하십시오.",
"An unknown error occurred": "알 수없는 오류가 발생했습니다",
"You were not allowed to connect": "이 기기는 연결이 허용되지 않았습니다",
"You were disconnected": "연결이 해제되었습니다",
"WebRTC error occurred": "WebRTC 오류가 발생했습니다",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "오픈소스 프로젝트에 재정적으로 기여하는 것은 더 좋은 프로그램 개발 동기를 부여합니다.",
"Donate": "기부하기",
"get-deskreen-pro": "Deskreen Pro 받기",
"get-deskreen-pro-tooltip": "Deskreen Pro 받기 - 다운로드 페이지를 엽니다.",
"Video stream is paused": "비디오 스트림이 일시 중지됩니다",
"Video stream is playing": "비디오 스트림이 재생 중입니다",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "전체 화면 종료 후 비디오 스트림이 일시 중지되었습니다. 계속하려면 재생을 클릭하세요.",
"Pause": "중지",
"Play": "재생",
"Video Settings": "비디오 설정",
"Flip": "화면 좌우 반전",
"Video quality has been changed to": "비디오 품질이 변경되었습니다",
"Click to Open Video Settings": "비디오 설정 열기",
"Click to Enter Full Screen Mode": "전체 화면 모드로 들어가려면 클릭하십시오",
"Click to Play Video": "비디오 재생을 클릭하십시오",
"Click to Pause Video": "비디오 일시 정지를 클릭하십시오",
"Default video player has been turned OFF": "기본 비디오 플레이어가 꺼져 있습니다",
"Default video player has been turned ON": "기본 비디오 플레이어가 켜져 있습니다",
"ON": "켜짐",
"OFF": "꺼짐",
"Default Video Player": "기본 비디오 플레이어",
"Click to visit our website": "클릭하면 웹사이트를 방문합니다",
"Video is flipped horizontally": "비디오를 수평으로 뒤집습니다",
"flip-the-screen-is-pro-version-only": "화면 뒤집기는 Pro 버전에서만 사용할 수 있습니다",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "연결 정보를 보려면 클릭하십시오",
"Pair ID": "Pair ID",
"Unpair": "Unpair",
"Session ID": "Session ID",
"Click to boost video stream if it is lagging": "클릭하면 비디오 스트림을 향상시킬 수 있습니다",
"Privacy Notice: Analytics in Deskreen CE Viewer": "개인정보 안내: Deskreen CE Viewer의 분석",
"Analytics Reference": "분석 참조",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "이 앱은 Google Analytics(구글에서 제공하는 무료 서비스)를 사용하여 기본 사용 데이터를 익명으로 추적합니다. 이를 통해 사람들이 앱을 어떻게 사용하는지 이해하고 모두를 위해 개선할 수 있습니다.",
"What we collect:": "수집하는 정보:",
"Page views (which screens you visit)": "페이지 조회수(어떤 화면을 방문하는지)",
"Time spent on pages": "페이지에 머문 시간",
"Basic device info (browser type, screen size)": "기본 기기 정보(브라우저 종류, 화면 크기)",
"Your IP address (anonymized — last part removed for privacy)": "IP 주소(익명 처리됨 — 개인정보 보호를 위해 마지막 부분이 제거됩니다)",
"What we DON'T collect:": "수집하지 않는 정보:",
"Personal info (names, emails, passwords)": "개인 정보(이름, 이메일, 비밀번호)",
"Exact location": "정확한 위치",
"Any files or content you interact with": "사용자가 상호작용하는 파일이나 콘텐츠",
"Why anonymous?": "왜 익명으로 수집하나요?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "IP 주소는 자동으로 축약되며, 이 데이터로 개인을 식별할 수 없습니다.",
"Your options:": "선택 사항:",
"Continue:": "계속:",
"We'll track anonymized usage to help improve the app.": "앱을 개선하기 위해 익명화된 사용 데이터를 추적합니다.",
"Opt out:": "거부:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "아래의 거부 버튼을 클릭하여 추적을 비활성화하세요. (이 선택을 존중하지만, 공동 피드백에 기반한 향후 개선 사항을 놓칠 수 있습니다.)",
"Data goes to: Google Analytics. See their privacy policy.": "데이터가 전송되는 곳: Google Analytics. 개인정보 보호정책을 확인하세요.",
"Accept": "동의",
"Allow": "허용",
"Deny": "거부",
"re-initiate-connection": "연결 재시작",
"Privacy Settings": "개인정보 설정",
"Change your preference:": "선호도 변경:",
"Enable analytics:": "분석 활성화:",
"Disable analytics:": "분석 비활성화:",
"Enable Analytics": "분석 활성화",
"Disable Analytics": "분석 비활성화",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "아래의 비활성화 버튼을 클릭하여 추적을 중지하세요. (이 선택을 존중하지만, 집단 피드백을 기반으로 한 향후 개선 사항을 놓칠 수 있습니다.)",
"their privacy policy": "개인정보 보호정책"
}
================================================
FILE: src/client-viewer/public/locales/nl/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "Wachtend op de gebruiker om de TOESTAAN knop in te drukken op het scherm-delen-apparaat...",
"Waiting for user to select source to share from screen sharing device...": "Wachtend op de gebruiker om de bron te selecteren om te delen vanuit het scherm-delen-apparaat...",
"My Device Info": "Mijn Apparaat Info",
"Device Type": "Apparaat Type",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "Uw Apparaat IP zou identiek moeten zijn met het Apparaat IP in de verschenen alert pop-up op uw computer, waar Deskreen-CE actief is",
"Device IP": "Apparaat IP",
"Device Browser": "Apparaat Browser",
"Device OS": "Apparaat OS",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "Deze details zouden identiek moeten zijn met diegene die u ziet in de alert pop-up op uw computer, waar Deskreen-CE actief is",
"Deskreen-CE Screen Viewer": "Deskreen-CE Scherm Viewer",
"Connected!": "Verbonden!",
"Error occurred": "Fout opgetreden",
"Deskreen-CE Error Dialog": "Deskreen-CE Error Dialoog",
"Something went wrong": "Er is iets misgegaan",
"You may close this browser window then try to connect again": "U mag dit browser venster sluiten en opnieuw proberen te verbinden",
"An unknown error occurred": "Een onbekende fout is opgetreden",
"You were not allowed to connect": "Uw verbinding werd niet toegestaan",
"You were disconnected": "Uw verbinding werd verbroken",
"WebRTC error occurred": "WebRTC fout opgetreden",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "Als u Deskreen-CE waardeert, overweeg dan een financiële bijdrage. Deskreen-CE is open-source. Uw donaties houden ons gemotiveerd om Deskreen-CE te blijven verbeteren.",
"Donate": "Doneer",
"get-deskreen-pro": "Ontvang Deskreen Pro",
"get-deskreen-pro-tooltip": "Ontvang Deskreen Pro - opent de downloadpagina.",
"Video stream is paused": "Video stream is gepauzeerd",
"Video stream is playing": "Video stream wordt afgespeeld",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "Video stream is gepauzeerd na het verlaten van de volledig scherm modus. Klik op Afspelen om door te gaan.",
"Pause": "Pauze",
"Play": "Afspelen",
"Video Settings": "Video Instellingen",
"Flip": "Flip",
"Video quality has been changed to": "Video kwaliteit is aangepast naar",
"Click to Open Video Settings": "Klik om Video Instellingen te openen",
"Click to Enter Full Screen Mode": "Klik om Volledig Scherm modus te activeren",
"Click to Play Video": "Klik om video af te spelen",
"Click to Pause Video": "Klik om video te pauzeren",
"Default video player has been turned OFF": "Standaard video speler staat nu UIT",
"Default video player has been turned ON": "Standaard video speler staat nu AAN",
"ON": "AAN",
"OFF": "UIT",
"Default Video Player": "Standaard Video Speler",
"Click to visit our website": "Klik om onze website te bezoeken",
"Video is flipped horizontally": "Video is horizontaal geflipt",
"flip-the-screen-is-pro-version-only": "Scherm omdraaien is alleen beschikbaar in de Pro-versie",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "Klik om verbindings informatie te zien",
"Pair ID": "Koppel ID",
"Unpair": "Ontkoppelen",
"Session ID": "Sessie ID",
"Click to boost video stream if it is lagging": "Klik om de video stream te versterken als het traag is",
"Privacy Notice: Analytics in Deskreen CE Viewer": "Privacyverklaring: Analyse in Deskreen CE Viewer",
"Analytics Reference": "Analytiek Referentie",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "Deze app gebruikt Google Analytics (een gratis dienst van Google) om anoniem basisgebruikgegevens bij te houden. Zo begrijpen we hoe de app wordt gebruikt en kunnen we haar voor iedereen verbeteren.",
"What we collect:": "Wat we verzamelen:",
"Page views (which screens you visit)": "Paginaweergaven (welke schermen je bezoekt)",
"Time spent on pages": "Tijd doorgebracht op pagina's",
"Basic device info (browser type, screen size)": "Basisapparaatinformatie (browsertype, schermgrootte)",
"Your IP address (anonymized — last part removed for privacy)": "Je IP-adres (geanonimiseerd — het laatste deel wordt verwijderd voor je privacy)",
"What we DON'T collect:": "Wat we NIET verzamelen:",
"Personal info (names, emails, passwords)": "Persoonlijke info (namen, e-mails, wachtwoorden)",
"Exact location": "Exacte locatie",
"Any files or content you interact with": "Bestanden of inhoud waarmee je interacteert",
"Why anonymous?": "Waarom anoniem?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "Je IP wordt automatisch ingekort, zodat niemand je persoonlijk kan identificeren met deze gegevens.",
"Your options:": "Je opties:",
"Continue:": "Doorgaan:",
"We'll track anonymized usage to help improve the app.": "We volgen geanonimiseerd gebruik om de app te verbeteren.",
"Opt out:": "Afmelden:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Klik op de knop Weigeren hieronder om tracking uit te schakelen. (We respecteren die keuze, maar je kunt toekomstige verbeteringen missen die op collectieve feedback zijn gebaseerd.)",
"Data goes to: Google Analytics. See their privacy policy.": "Gegevens gaan naar: Google Analytics. Bekijk hun privacybeleid.",
"Accept": "Accepteren",
"Allow": "Toestaan",
"Deny": "Weigeren",
"re-initiate-connection": "Verbinding opnieuw starten",
"Privacy Settings": "Privacy-instellingen",
"Change your preference:": "Wijzig uw voorkeur:",
"Enable analytics:": "Analytics inschakelen:",
"Disable analytics:": "Analytics uitschakelen:",
"Enable Analytics": "Analytics inschakelen",
"Disable Analytics": "Analytics uitschakelen",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Klik op de knop Uitschakelen hieronder om tracking te stoppen. (We respecteren deze keuze, maar u kunt toekomstige verbeteringen missen op basis van collectieve feedback.)",
"their privacy policy": "hun privacybeleid"
}
================================================
FILE: src/client-viewer/public/locales/ru/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "Ждем когда пользователь нажмет кнопку РАЗРЕШИТЬ для доступа к экрану компьютера...",
"Waiting for user to select source to share from screen sharing device...": "Ждем когда пользователь выберет Весь экран или Окно приложения для отображения его здесь...",
"My Device Info": "Информация о моем устройстве",
"Device Type": "Тип устройства",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "IP-aдрес вашего устройства должен совпадать с «IP-адресом устройства» во всплывающем окне с предупреждением на компьютере, где работает Deskreen-CE.",
"Device IP": "IP-aдрес устройства",
"Device Browser": "Веб-браузер устройства",
"Device OS": "ОС устройства",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "Эти данные должны совпадать с теми, которые вы видите во всплывающем окне предупреждения на экране компьютера, на котором работает Deskreen-CE.",
"Deskreen-CE Screen Viewer": "Просмотрщик экрана Deskreen-CE",
"Connected!": "Подключено!",
"Error occurred": "Произошла ошибка",
"Deskreen-CE Error Dialog": "Диалог ошибки Deskreen-CE",
"Something went wrong": "Произошло что-то не так",
"You may close this browser window then try to connect again": "Вы можете закрыть это окно браузера и попытаться подключиться снова",
"An unknown error occurred": "Произошла неизвестная ошибка",
"You were not allowed to connect": "Вам не разрешили подключиться",
"You were disconnected": "Вы были отключены",
"WebRTC error occurred": "Произошла ошибка WebRTC",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "Если вам нравится Deskreen-CE, подумайте о том, чтобы внести финансовый вклад. Deskreen-CE - это оупенсорсный проэкт. Ваши пожертвования позволяют нам делать Deskreen-CE еще лучше.",
"Donate": "Пожертвовать",
"get-deskreen-pro": "Получить Deskreen Pro",
"get-deskreen-pro-tooltip": "Получить Deskreen Pro - открывает страницу загрузки.",
"Video stream is paused": "Видеопоток приостановлен",
"Video stream is playing": "Видеопоток воспроизводится",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "Видеопоток приостановлен после выхода из полноэкранного режима. Пожалуйста, нажмите Воспроизвести, чтобы продолжить.",
"Pause": "Pause",
"Play": "Play",
"Video Settings": "Настройки видео",
"Flip": "Отзеркалить",
"Video quality has been changed to": "Качество видео изменено на",
"Click to Open Video Settings": "Нажмите, чтобы открыть настройки видео",
"Click to Enter Full Screen Mode": "Нажмите, чтобы перейти в полноэкранный режим",
"Click to Play Video": "Нажмите, чтобы воспроизвести видео",
"Click to Pause Video": "Нажмите, чтобы приостановить видео",
"Default video player has been turned OFF": "Видеоплеер по умолчанию отключен",
"Default video player has been turned ON": "Видеопроигрыватель по умолчанию включен",
"ON": "ВКЛ",
"OFF": "ВЫКЛ",
"Default Video Player": "Видеоплеер по умолчанию",
"Click to visit our website": "Нажмите, чтобы посетить наш сайт",
"Video is flipped horizontally": "Видео отзеркалено",
"flip-the-screen-is-pro-version-only": "Отзеркаливание экрана доступно только в Pro версии",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "Click to see connection info",
"Pair ID": "Pair ID",
"Unpair": "Unpair",
"Session ID": "Session ID",
"Click to boost video stream if it is lagging": "Click to boost video stream if it is lagging",
"Privacy Notice: Analytics in Deskreen CE Viewer": "Уведомление о конфиденциальности: аналитика в Deskreen CE Viewer",
"Analytics Reference": "Справочник по аналитике",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "Это приложение использует Google Analytics (бесплатный сервис Google), чтобы анонимно отслеживать базовые данные использования. Это помогает нам понимать, как люди пользуются приложением, чтобы улучшать его для всех.",
"What we collect:": "Что мы собираем:",
"Page views (which screens you visit)": "Просмотры страниц (какие экраны вы посещаете)",
"Time spent on pages": "Время, проведённое на страницах",
"Basic device info (browser type, screen size)": "Базовую информацию об устройстве (тип браузера, размер экрана)",
"Your IP address (anonymized — last part removed for privacy)": "Ваш IP-адрес (анонимизированный — последняя часть удалена для защиты приватности)",
"What we DON'T collect:": "Чего мы НЕ собираем:",
"Personal info (names, emails, passwords)": "Персональные данные (имена, электронные адреса, пароли)",
"Exact location": "Точное местоположение",
"Any files or content you interact with": "Любые файлы или контент, с которыми вы взаимодействуете",
"Why anonymous?": "Почему анонимно?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "Ваш IP автоматически сокращается, и никто не сможет идентифицировать вас лично по этим данным.",
"Your options:": "Ваши варианты:",
"Continue:": "Продолжить:",
"We'll track anonymized usage to help improve the app.": "Мы будем отслеживать анонимизированное использование, чтобы улучшать приложение.",
"Opt out:": "Отказаться:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Нажмите кнопку Отклонить ниже, чтобы отключить отслеживание. (Мы уважим этот выбор, но вы можете пропустить будущие улучшения, основанные на коллективной обратной связи.)",
"Data goes to: Google Analytics. See their privacy policy.": "Данные отправляются в: Google Analytics. Ознакомьтесь с их политикой конфиденциальности.",
"Accept": "Принять",
"Allow": "Разрешить",
"Deny": "Отклонить",
"re-initiate-connection": "Повторить подключение",
"Privacy Settings": "Настройки конфиденциальности",
"Change your preference:": "Изменить ваше предпочтение:",
"Enable analytics:": "Включить аналитику:",
"Disable analytics:": "Отключить аналитику:",
"Enable Analytics": "Включить аналитику",
"Disable Analytics": "Отключить аналитику",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Нажмите кнопку Отключить ниже, чтобы остановить отслеживание. (Мы уважаем этот выбор, но вы можете пропустить будущие улучшения, основанные на коллективной обратной связи.)",
"their privacy policy": "их политику конфиденциальности"
}
================================================
FILE: src/client-viewer/public/locales/sv/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "Väntar på att användaren ska klicka på 'TILLÅT' på skärmdelningsenheten...",
"Waiting for user to select source to share from screen sharing device...": "Väntar på att användaren ska välja källa att dela från skärmdelningsenhet...",
"My Device Info": "Min enhetsinformation",
"Device Type": "Enhetens typ",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "Din enhets IP-adress bör matcha med 'Enhetens IP' i den varnings-popup som dyker upp på din dator där Deskreen-CE körs",
"Device IP": "Enhetens IP",
"Device Browser": "Enhetens webbläsare",
"Device OS": "Enhetens operativsystem",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "Dessa uppgifter ska matcha de som du ser i popup-fönstret på skärmdelningsenheten.",
"Deskreen-CE Screen Viewer": "Deskreen-CE skärmvisare",
"Connected!": "Ansluten!",
"Error occurred": "Ett fel inträffade",
"Deskreen-CE Error Dialog": "Deskreen-CE felhanterare",
"Something went wrong": "Något blev fel",
"You may close this browser window then try to connect again": "Stäng det här webbläsarfönstret och försök sedan ansluta igen",
"An unknown error occurred": "Ett okänt fel inträffade",
"You were not allowed to connect": "Du fick inte ansluta",
"You were disconnected": "Du blev nedkopplad",
"WebRTC error occurred": "Ett WebRTC-fel error inträffade",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "Om du gillar Deskreen-CE, överväg i så fall att ge oss ett ekonomiskt bidrag. Deskreen-CE är open-source. Era donationer motiverar oss att göra Deskreen-CE ännu bättre.",
"Donate": "Donera",
"get-deskreen-pro": "Hämta Deskreen Pro",
"get-deskreen-pro-tooltip": "Hämta Deskreen Pro - öppnar nedladdningssidan.",
"Video stream is paused": "Videoströmmen är pausad",
"Video stream is playing": "Videoströmmen spelas",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "Videoströmmen pausades efter att ha lämnat helskärmsläge. Klicka på Kör för att fortsätta.",
"Pause": "Paus",
"Play": "Kör",
"Video Settings": "Videoinställningar",
"Flip": "Omvänd",
"Video quality has been changed to": "Videokvaliteten har ändrats till",
"Click to Open Video Settings": "Klicka här för att öppna videoinställningarna",
"Click to Enter Full Screen Mode": "Klicka här för att gå in i helskärmsläge",
"Click to Play Video": "Klicka för att spela upp video",
"Click to Pause Video": "Klicka för att pausa video",
"Default video player has been turned OFF": "Standardvideospelaren har stängts av",
"Default video player has been turned ON": "Standardvideospelaren har aktiverats",
"ON": "PÅ",
"OFF": "AV",
"Default Video Player": "Standardvideospelare",
"Click to visit our website": "Klicka här för att besöka vår webbplats",
"Video is flipped horizontally": "Videon är vänd horisontellt",
"flip-the-screen-is-pro-version-only": "Vända skärmen är endast tillgängligt i Pro-versionen",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "Klicka här för att visa anslutningsinformationen",
"Pair ID": "ID för sammankopplingen",
"Unpair": "Ta bort sammankopplingen",
"Session ID": "ID för sessionen",
"Click to boost video stream if it is lagging": "Klicka för att öka videoströmmen om den släpar efter",
"Privacy Notice: Analytics in Deskreen CE Viewer": "Integritetsmeddelande: Analys i Deskreen CE Viewer",
"Analytics Reference": "Analysreferens",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "Den här appen använder Google Analytics (en kostnadsfri tjänst från Google) för att anonymt spåra grundläggande användningsdata. Det hjälper oss att förstå hur appen används så att vi kan förbättra den för alla.",
"What we collect:": "Det vi samlar in:",
"Page views (which screens you visit)": "Sidvisningar (vilka vyer du besöker)",
"Time spent on pages": "Tid som spenderas på sidor",
"Basic device info (browser type, screen size)": "Grundläggande enhetsinformation (webbläsartyp, skärmstorlek)",
"Your IP address (anonymized — last part removed for privacy)": "Din IP-adress (anonymiserad — den sista delen tas bort av integritetsskäl)",
"What we DON'T collect:": "Det vi INTE samlar in:",
"Personal info (names, emails, passwords)": "Personlig information (namn, e-postadresser, lösenord)",
"Exact location": "Exakt plats",
"Any files or content you interact with": "Filer eller innehåll du interagerar med",
"Why anonymous?": "Varför anonymt?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "Din IP-adress förkortas automatiskt, och ingen kan identifiera dig personligen utifrån dessa data.",
"Your options:": "Dina alternativ:",
"Continue:": "Fortsätt:",
"We'll track anonymized usage to help improve the app.": "Vi spårar anonymiserad användning för att hjälpa oss förbättra appen.",
"Opt out:": "Avslå:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Klicka på knappen Avböj nedan för att inaktivera spårning. (Vi respekterar detta val, men du kan gå miste om framtida förbättringar som bygger på samlad feedback.)",
"Data goes to: Google Analytics. See their privacy policy.": "Data skickas till: Google Analytics. Se deras integritetspolicy.",
"Accept": "Acceptera",
"Allow": "Tillåt",
"Deny": "Avböj",
"re-initiate-connection": "Återanslut",
"Privacy Settings": "Integritetsinställningar",
"Change your preference:": "Ändra din preferens:",
"Enable analytics:": "Aktivera analys:",
"Disable analytics:": "Inaktivera analys:",
"Enable Analytics": "Aktivera analys",
"Disable Analytics": "Inaktivera analys",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Klicka på knappen Inaktivera nedan för att stoppa spårning. (Vi respekterar detta val, men du kan missa framtida förbättringar baserade på kollektiv feedback.)",
"their privacy policy": "deras integritetspolicy"
}
================================================
FILE: src/client-viewer/public/locales/ua/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "Чекаємо коли користувач натисне кнопку ДОЗВОЛИТИ для доступу до екрану комп'ютера...",
"Waiting for user to select source to share from screen sharing device...": "Чекаємо коли користувач вибере Весь екран або Вікно додатка для відображення його тут...",
"My Device Info": "Інформація про мій пристрій",
"Device Type": "Тип пристрою",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "IP-aдрес пристрою вашого пристрою має збігатися з «IP-адресою пристрою» у спливаючому вікні сповіщення, що з’явилося на комп’ютері, де працює Deskreen-CE.",
"Device IP": "IP-aдрес пристрою",
"Device Browser": "Веб-браузер пристрою",
"Device OS": "ОС пристрою",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "Ці деталі повинні збігатися з тими, які ви бачите у спливаючому вікні сповіщень на екрані комп’ютера, де запущений Deskreen-CE.",
"Deskreen-CE Screen Viewer": "Переглядач екрану Deskreen-CE",
"Connected!": "Підключено!",
"Error occurred": "Виникла помилка",
"Deskreen-CE Error Dialog": "Діалог помилки Deskreen-CE",
"Something went wrong": "Щось не так сталося",
"You may close this browser window then try to connect again": "Ви можете закрити це вікно браузера та спробувати підключитися знову",
"An unknown error occurred": "Виникла невідома помилка",
"You were not allowed to connect": "Вам не дозволили підключитися",
"You were disconnected": "Ви були відключені",
"WebRTC error occurred": "Сталася помилка WebRTC",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "Якщо вам подобається Deskreen-CE, подумайте про те, щоб внести фінансовий внесок. Deskreen-CE - це оупенсорсний проект. Ваші пожертвування дозволяють нам робити Deskreen-CE ще краще.",
"Donate": "Пожертвувати",
"get-deskreen-pro": "Отримати Deskreen Pro",
"get-deskreen-pro-tooltip": "Отримати Deskreen Pro - відкриває сторінку завантаження.",
"Video stream is paused": "Відеопотік призупинено",
"Video stream is playing": "Відеопотік продовжується",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "Відеопотік призупинено після виходу з повноекранного режиму. Будь ласка, натисніть Відтворення, щоб продовжити.",
"Pause": "Pause",
"Play": "Play",
"Video Settings": "Настройки видео",
"Flip": "Віддзеркалити",
"Video quality has been changed to": "Якість відео змінено на",
"Click to Open Video Settings": "Натисніть, щоб відкрити настройки відео",
"Click to Enter Full Screen Mode": "Натисніть для входу в повноекранноий режим",
"Click to Play Video": "Натисніть, щоб відтворити відео",
"Click to Pause Video": "Натисніть, щоб призупинити відео",
"Default video player has been turned OFF": "Стандартний відеоплеєр браузера вимкнено",
"Default video player has been turned ON": "Стандартний відеоплеєр браузера включений",
"ON": "ВКЛ",
"OFF": "ВИМК",
"Default Video Player": "Стандартний відеоплеєр браузера",
"Click to visit our website": "Клацніть, щоб відвідати наш веб-сайт",
"Video is flipped horizontally": "Відео віддзеркалено",
"flip-the-screen-is-pro-version-only": "Віддзеркалення екрану доступне лише в Pro версії",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "",
"Click to see connection info": "Click to see connection info",
"Pair ID": "Pair ID",
"Unpair": "Unpair",
"Session ID": "Session ID",
"Click to boost video stream if it is lagging": "Click to boost video stream if it is lagging",
"Privacy Notice: Analytics in Deskreen CE Viewer": "Повідомлення про конфіденційність: аналітика в Deskreen CE Viewer",
"Analytics Reference": "Довідник з аналітики",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "Цей застосунок використовує Google Analytics (безкоштовний сервіс Google), щоб анонімно відстежувати базові дані використання. Це допомагає нам розуміти, як люди користуються застосунком, аби покращувати його для всіх.",
"What we collect:": "Що ми збираємо:",
"Page views (which screens you visit)": "Перегляди сторінок (які екрани ви відвідуєте)",
"Time spent on pages": "Час, проведений на сторінках",
"Basic device info (browser type, screen size)": "Базову інформацію про пристрій (тип браузера, розмір екрана)",
"Your IP address (anonymized — last part removed for privacy)": "Вашу IP-адресу (анонімізовану — остання частина вилучається для приватності)",
"What we DON'T collect:": "Що ми НЕ збираємо:",
"Personal info (names, emails, passwords)": "Особисту інформацію (імена, електронні адреси, паролі)",
"Exact location": "Точне місцезнаходження",
"Any files or content you interact with": "Будь-які файли чи вміст, з якими ви взаємодієте",
"Why anonymous?": "Чому анонімно?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "Вашу IP автоматично скорочують, тому ніхто не може ідентифікувати вас особисто за цими даними.",
"Your options:": "Ваші варіанти:",
"Continue:": "Продовжити:",
"We'll track anonymized usage to help improve the app.": "Ми відстежуватимемо анонімізоване використання, щоб допомогти покращити застосунок.",
"Opt out:": "Відмовитися:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Натисніть кнопку Відхилити нижче, щоб вимкнути відстеження. (Ми поважаємо цей вибір, але ви можете пропустити майбутні покращення, що базуються на спільному зворотному зв'язку.)",
"Data goes to: Google Analytics. See their privacy policy.": "Дані надсилаються до: Google Analytics. Перегляньте їхню політику конфіденційності.",
"Accept": "Погодитися",
"Allow": "Дозволити",
"Deny": "Відхилити",
"re-initiate-connection": "Повторно підключитися",
"Privacy Settings": "Налаштування конфіденційності",
"Change your preference:": "Змінити вашу перевагу:",
"Enable analytics:": "Увімкнути аналітику:",
"Disable analytics:": "Вимкнути аналітику:",
"Enable Analytics": "Увімкнути аналітику",
"Disable Analytics": "Вимкнути аналітику",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "Натисніть кнопку Вимкнути нижче, щоб зупинити відстеження. (Ми поважатимемо цей вибір, але ви можете пропустити майбутні покращення, засновані на колективному відгуку.)",
"their privacy policy": "їхню політику конфіденційності"
}
================================================
FILE: src/client-viewer/public/locales/zh_CN/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "正在等待用户单击屏幕共享设备上的允许按钮...",
"Waiting for user to select source to share from screen sharing device...": "正在等待用户从屏幕共享设备选择要共享的源...",
"My Device Info": "我的设备信息",
"Device Type": "设备类型",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "您的设备 IP 应该与运行 Deskreen-CE 的计算机上出现的警报弹出窗口中的 '设备 IP' 相匹配。",
"Device IP": "设备 IP",
"Device Browser": "设备浏览器",
"Device OS": "设备操作系统",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "这些详细信息应与您在屏幕共享设备上的警报弹出窗口中看到的信息相匹配。",
"Deskreen-CE Screen Viewer": "Deskreen-CE 屏幕查看器",
"Connected!": "已连接!",
"Error occurred": "出现错误",
"Deskreen-CE Error Dialog": "Deskreen-CE 错误对话框",
"Something went wrong": "出问题了",
"You may close this browser window then try to connect again": "您可以关闭此浏览器窗口,然后尝试重新连接",
"An unknown error occurred": "出现未知错误",
"You were not allowed to connect": "您不能连接",
"You were disconnected": "您将断开连接",
"WebRTC error occurred": "出现 WebRTC 错误",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "如果你喜欢 Deskreen-CE,可以考虑出钱。Deskreen-CE 是开源的。您的捐赠使我们有动力让 Deskreen-CE 变得更好。",
"Donate": "捐赠",
"get-deskreen-pro": "获取 Deskreen Pro",
"get-deskreen-pro-tooltip": "获取 Deskreen Pro - 打开下载页面。",
"Video stream is paused": "视频流暂停",
"Video stream is playing": "正在播放视频流",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "退出全屏后视频流已暂停。请点击播放以继续。",
"Pause": "暂停",
"Play": "播放",
"Video Settings": "视频设置",
"Flip": "翻转",
"Video quality has been changed to": "视频质量已更改为",
"Click to Open Video Settings": "单击以打开视频设置",
"Click to Enter Full Screen Mode": "单击以进入全屏模式",
"Click to Play Video": "单击以播放视频",
"Click to Pause Video": "单击以暂停视频",
"Default video player has been turned OFF": "默认视频播放器已关闭",
"Default video player has been turned ON": "默认视频播放器已开启",
"ON": "开启",
"OFF": "关闭",
"Default Video Player": "默认视频播放器",
"Click to visit our website": "点击访问我们的网站",
"Video is flipped horizontally": "视频水平翻转",
"flip-the-screen-is-pro-version-only": "翻转屏幕仅在 Pro 版本中可用",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "以下翻译尚未添加到 UI,但欢迎您的翻译!功能将很快添加,因此需要您的翻译",
"Click to see connection info": "单击以查看连接信息",
"Pair ID": "配对 ID",
"Unpair": "取消配对",
"Session ID": "会话 ID",
"Click to boost video stream if it is lagging": "如果视频流滞后,请单击以提高视频流",
"Privacy Notice: Analytics in Deskreen CE Viewer": "隐私提示:Deskreen CE Viewer 中的分析",
"Analytics Reference": "分析参考",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "本应用使用 Google Analytics(Google 提供的免费服务)匿名跟踪基本的使用数据。这有助于我们了解用户如何使用应用,从而为所有人改进。",
"What we collect:": "我们收集:",
"Page views (which screens you visit)": "页面浏览量(你访问的界面)",
"Time spent on pages": "在页面停留的时间",
"Basic device info (browser type, screen size)": "设备基本信息(浏览器类型、屏幕尺寸)",
"Your IP address (anonymized — last part removed for privacy)": "你的 IP 地址(已匿名化——出于隐私保护会移除最后一部分)",
"What we DON'T collect:": "我们不会收集:",
"Personal info (names, emails, passwords)": "个人信息(姓名、邮箱、密码)",
"Exact location": "精确位置",
"Any files or content you interact with": "你接触的任何文件或内容",
"Why anonymous?": "为什么是匿名的?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "你的 IP 会自动被截短,任何人都无法通过这些数据识别你的身份。",
"Your options:": "你的选择:",
"Continue:": "继续:",
"We'll track anonymized usage to help improve the app.": "我们会跟踪匿名化的使用情况,以帮助改进应用。",
"Opt out:": "拒绝:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "点击下面的\"拒绝\"按钮以关闭跟踪。(我们会尊重这个选择,但你可能会错过基于集体反馈的未来改进。)",
"Data goes to: Google Analytics. See their privacy policy.": "数据将发送至:Google Analytics。查看其隐私政策。",
"Accept": "接受",
"Allow": "允许",
"Deny": "拒绝",
"re-initiate-connection": "重新连接",
"Privacy Settings": "隐私设置",
"Change your preference:": "更改您的偏好:",
"Enable analytics:": "启用分析:",
"Disable analytics:": "禁用分析:",
"Enable Analytics": "启用分析",
"Disable Analytics": "禁用分析",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "点击下面的禁用按钮以停止跟踪。(我们会尊重您的选择,但您可能会错过基于集体反馈的未来改进。)",
"their privacy policy": "其隐私政策"
}
================================================
FILE: src/client-viewer/public/locales/zh_TW/translation.json
================================================
{
"Waiting for user to click ALLOW button on screen sharing device...": "正在等待使用者單擊螢幕共享裝置上的允許按鈕...",
"Waiting for user to select source to share from screen sharing device...": "正在等待使用者從螢幕共享裝置選擇要共享的源...",
"My Device Info": "我的裝置資訊",
"Device Type": "裝置型別",
"Your Device IP should match with Device IP in alert popup appeared on your computer, where Deskreen-CE is running": "您的裝置 IP 應該與執行 Deskreen-CE 的計算機上出現的警報彈出視窗中的 '裝置 IP' 相匹配。",
"Device IP": "裝置 IP",
"Device Browser": "裝置瀏覽器",
"Device OS": "裝置作業系統",
"These details should match with the ones that you see in alert popup on computer screen, where Deskreen-CE is running": "這些詳細資訊應與您在螢幕共享裝置上的警報彈出視窗中看到的資訊相匹配。",
"Deskreen-CE Screen Viewer": "Deskreen-CE 螢幕檢視器",
"Connected!": "已連線!",
"Error occurred": "出現錯誤",
"Deskreen-CE Error Dialog": "Deskreen-CE 錯誤對話方塊",
"Something went wrong": "出問題了",
"You may close this browser window then try to connect again": "您可以關閉此瀏覽器視窗,然後嘗試重新連線",
"An unknown error occurred": "出現未知錯誤",
"You were not allowed to connect": "您不能連線",
"You were disconnected": "您將斷開連線",
"WebRTC error occurred": "出現 WebRTC 錯誤",
"If you like Deskreen-CE consider contributing financially Deskreen-CE is open-source Your donations keep us motivated to make Deskreen-CE even better": "如果你喜歡 Deskreen-CE,可以考慮出錢。Deskreen-CE 是開源的。您的捐贈使我們有動力讓 Deskreen-CE 變得更好。",
"Donate": "捐贈",
"get-deskreen-pro": "取得 Deskreen Pro",
"get-deskreen-pro-tooltip": "取得 Deskreen Pro - 開啟下載頁面。",
"Video stream is paused": "影片流暫停",
"Video stream is playing": "正在播放影片流",
"Video stream paused after exiting fullscreen. Please click Play to continue.": "退出全螢幕後影片流已暫停。請點擊播放以繼續。",
"Pause": "暫停",
"Play": "播放",
"Video Settings": "影片設定",
"Flip": "翻轉",
"Video quality has been changed to": "影片質量已更改為",
"Click to Open Video Settings": "單擊以開啟影片設定",
"Click to Enter Full Screen Mode": "單擊以進入全屏模式",
"Click to Play Video": "單擊以播放影片",
"Click to Pause Video": "單擊以暫停影片",
"Default video player has been turned OFF": "預設影片播放器已關閉",
"Default video player has been turned ON": "預設影片播放器已開啟",
"ON": "開啟",
"OFF": "關閉",
"Default Video Player": "預設影片播放器",
"Click to visit our website": "點選訪問我們的網站",
"Video is flipped horizontally": "影片水平翻轉",
"flip-the-screen-is-pro-version-only": "翻轉螢幕僅在 Pro 版本中可用",
"TRANSLATIONS BELOW ARE NOT ADDED TO UI YET, BUT YOUR TRANSLATIONS ARE WELCOME! THE FEATURES WILL BE ADDED SOON SO YOUR TRANSLATIONS ARE NEEDED": "以下翻譯尚未新增到 UI,但歡迎您的翻譯!功能將很快新增,因此需要您的翻譯",
"Click to see connection info": "單擊以檢視連線資訊",
"Pair ID": "配對 ID",
"Unpair": "取消配對",
"Session ID": "會話 ID",
"Click to boost video stream if it is lagging": "如果影片流滯後,請單擊以提高影片流",
"Privacy Notice: Analytics in Deskreen CE Viewer": "隱私提示:Deskreen CE Viewer 的分析",
"Analytics Reference": "分析參考",
"This app uses Google Analytics (a free service by Google) to anonymously track basic usage data. This helps us understand how people use the app so we can improve it for everyone.": "此應用程式使用 Google Analytics(Google 提供的免費服務)以匿名方式追蹤基本使用資料。這有助於我們了解使用者如何使用應用程式,從而為所有人帶來改進。",
"What we collect:": "我們收集:",
"Page views (which screens you visit)": "頁面瀏覽量(你造訪的畫面)",
"Time spent on pages": "在頁面停留的時間",
"Basic device info (browser type, screen size)": "裝置基本資訊(瀏覽器類型、螢幕尺寸)",
"Your IP address (anonymized — last part removed for privacy)": "你的 IP 位址(已匿名化 — 為保護隱私會移除最後一段)",
"What we DON'T collect:": "我們不會收集:",
"Personal info (names, emails, passwords)": "個人資訊(姓名、電子郵件、密碼)",
"Exact location": "精確位置",
"Any files or content you interact with": "你互動的任何檔案或內容",
"Why anonymous?": "為什麼要匿名?",
"Your IP is automatically shortened, and no one can identify you personally from this data.": "你的 IP 會自動被截短,沒有人能根據這些資料識別你的身份。",
"Your options:": "你的選項:",
"Continue:": "繼續:",
"We'll track anonymized usage to help improve the app.": "我們會追蹤匿名化的使用情況,協助改進應用程式。",
"Opt out:": "拒絕:",
"Click the Deny button below to disable tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "點擊下方的「拒絕」按鈕以停用追蹤。(我們會尊重此選擇,但你可能會錯過基於集體回饋的未來改善。)",
"Data goes to: Google Analytics. See their privacy policy.": "資料傳送至:Google Analytics。查看其隱私權政策。",
"Accept": "接受",
"Allow": "允許",
"Deny": "拒絕",
"re-initiate-connection": "重新連線",
"Privacy Settings": "隱私設定",
"Change your preference:": "更改您的偏好:",
"Enable analytics:": "啟用分析:",
"Disable analytics:": "停用分析:",
"Enable Analytics": "啟用分析",
"Disable Analytics": "停用分析",
"Click the Disable button below to stop tracking. (We'll respect this choice, but you might miss out on future improvements based on collective feedback.)": "點擊下面的停用按鈕以停止追蹤。(我們會尊重您的選擇,但您可能會錯過基於集體反饋的未來改進。)",
"their privacy policy": "其隱私權政策"
}
================================================
FILE: src/client-viewer/public/manifest.json
================================================
{
"short_name": "Deskreen CE",
"name": "Deskreen CE Makes Any Device a Second Screen For Your Computer",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
================================================
FILE: src/client-viewer/public/robots.txt
================================================
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
================================================
FILE: src/client-viewer/scripts/ga-interceptor.js
================================================
(function () {
const CONSENT_KEY = 'deskreen_ga_consent';
const GA_DOMAINS = [
'google-analytics.com',
'googletagmanager.com',
'google-analytics.co',
'analytics.google.com',
];
for (let i = 1; i <= 20; i++) {
GA_DOMAINS.push('region' + i + '.google-analytics.com');
}
function getConsentStatus() {
try {
const stored = localStorage.getItem(CONSENT_KEY);
return stored === 'accepted' ? 'accepted' : null;
} catch {
return null;
}
}
function isGoogleAnalyticsUrl(url) {
try {
const urlObj = new URL(url, window.location.href);
const hostname = urlObj.hostname.toLowerCase();
return GA_DOMAINS.some(function (domain) {
return hostname === domain || hostname.endsWith('.' + domain);
});
} catch {
return false;
}
}
function shouldBlockRequest() {
return getConsentStatus() !== 'accepted';
}
function isLocalIP(ip) {
const parts = ip.split('.').map(Number);
if (parts.length !== 4 || parts.some(isNaN)) {
return false;
}
// 127.0.0.0/8
if (parts[0] === 127) return true;
// 10.0.0.0/8
if (parts[0] === 10) return true;
// 172.16.0.0/12
if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) return true;
// 192.168.0.0/16
if (parts[0] === 192 && parts[1] === 168) return true;
return false;
}
function sanitizeGAUrl(url) {
try {
const urlObj = new URL(url);
// only sanitize /g/collect requests
if (!urlObj.pathname.includes('/g/collect')) {
return url;
}
const dlParam = urlObj.searchParams.get('dl');
if (!dlParam) {
return url;
}
try {
const dlUrl = new URL(decodeURIComponent(dlParam));
const hostname = dlUrl.hostname;
if (isLocalIP(hostname)) {
urlObj.searchParams.set('dl', encodeURIComponent('http://localhost'));
return urlObj.toString();
}
} catch {
// if dl parameter is not a valid URL, leave it as is
}
return url;
} catch {
return url;
}
}
// intercept fetch
if (window.fetch) {
const originalFetch = window.fetch;
window.fetch = function (input, init) {
let url =
typeof input === 'string'
? input
: input instanceof Request
? input.url
: '';
if (isGoogleAnalyticsUrl(url)) {
if (shouldBlockRequest()) {
return Promise.reject(
new Error(
'Google Analytics request blocked: user consent not granted',
),
);
}
url = sanitizeGAUrl(url);
if (input instanceof Request) {
input = new Request(url, init || input);
} else {
input = url;
}
}
return originalFetch.apply(this, arguments);
};
}
// intercept XMLHttpRequest
if (window.XMLHttpRequest) {
const XHR = window.XMLHttpRequest;
const originalOpen = XHR.prototype.open;
const originalSend = XHR.prototype.send;
XHR.prototype.open = function (method, url, async, username, password) {
let urlString = typeof url === 'string' ? url : url.toString();
if (isGoogleAnalyticsUrl(urlString)) {
if (shouldBlockRequest()) {
throw new Error(
'Google Analytics request blocked: user consent not granted',
);
}
urlString = sanitizeGAUrl(urlString);
url = urlString;
}
this._interceptedUrl = urlString;
return originalOpen.apply(this, arguments);
};
XHR.prototype.send = function () {
const url = this._interceptedUrl || '';
if (isGoogleAnalyticsUrl(url) && shouldBlockRequest()) {
return;
}
return originalSend.apply(this, arguments);
};
}
// intercept sendBeacon
if (navigator.sendBeacon) {
const originalSendBeacon = navigator.sendBeacon;
navigator.sendBeacon = function (url, data) {
let urlString = typeof url === 'string' ? url : url.toString();
if (isGoogleAnalyticsUrl(urlString)) {
if (shouldBlockRequest()) {
return false;
}
urlString = sanitizeGAUrl(urlString);
url = urlString;
}
return originalSendBeacon.call(this, url, data);
};
}
})();
================================================
FILE: src/client-viewer/src/App.css
================================================
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
================================================
FILE: src/client-viewer/src/App.tsx
================================================
import React, { useEffect, useState } from 'react';
import MainView from './containers/MainView';
import PrivacyConsentDialog from './components/PrivacyConsentDialog';
import LoadingScreen from './components/LoadingScreen';
import {
getConsentStatus,
setConsentStatus,
loadGoogleAnalytics,
getGaTagIdFromMeta,
updateAnalyticsConsent,
} from './utils/analytics';
const App: React.FC = () => {
// Helper function to check for prerendering safely
const isCurrentlyPrerendering = () => {
// Check if 'document' and 'prerendering' property exist
return typeof document !== 'undefined' &&
typeof document.prerendering === 'boolean'
? document.prerendering
: false; // Default to false if the property doesn't exist
};
const [isTrulyVisible, setIsTrulyVisible] = useState(
!isCurrentlyPrerendering(),
);
const [showConsentDialog, setShowConsentDialog] = useState(false);
const [hasConsent, setHasConsent] = useState(false);
useEffect(() => {
// Only set up listeners if document.prerendering is supported
if (
typeof document !== 'undefined' &&
typeof document.prerendering === 'boolean'
) {
const handlePrerenderChange = () => {
// When the prerendering state changes, update isTrulyVisible
// It becomes true when document.prerendering is false (i.e., page is activated)
setIsTrulyVisible(!document.prerendering);
};
// If it was initially prerendering, listen for the change to activate.
// The { once: true } option is useful if you only care about the first activation.
// However, if a page could theoretically go back into a prerender state (less common for user navigation),
// you might remove { once: true } but then also need more complex logic.
// For the typical "prerender then activate" flow, { once: true } is fine.
if (document.prerendering) {
document.addEventListener('prerenderingchange', handlePrerenderChange, {
once: true,
});
}
return () => {
// Cleanup the event listener
document.removeEventListener(
'prerenderingchange',
handlePrerenderChange,
);
};
}
// If document.prerendering is not supported, isTrulyVisible is already true
// (due to the initial useState value and isCurrentlyPrerendering fallback),
// so no specific effect logic is needed for that case here.
}, []); // Empty dependency array means this effect runs once on mount and cleans up on unmount.
useEffect(() => {
if (!isTrulyVisible) {
return;
}
// check consent status first
const consentStatus = getConsentStatus();
// load GA immediately when page is visible (before consent)
const gaTagId = getGaTagIdFromMeta();
if (gaTagId && gaTagId !== '%VITE_CLIENT_VIEWER_GA_TAG%') {
loadGoogleAnalytics(gaTagId);
// if user previously accepted consent, ensure page_view is sent
if (consentStatus === 'accepted') {
// wait a bit for GA to load, then update consent and send page_view
setTimeout(() => {
updateAnalyticsConsent('accepted');
}, 500);
}
}
if (consentStatus === 'accepted') {
setHasConsent(true);
} else if (consentStatus === 'opted-out') {
// user previously opted out - allow app usage without analytics
setHasConsent(true);
// ensure analytics consent is set to denied
updateAnalyticsConsent('opted-out');
} else {
// no consent yet - show dialog
setShowConsentDialog(true);
}
}, [isTrulyVisible]);
const handleAccept = () => {
setConsentStatus('accepted');
setShowConsentDialog(false);
setHasConsent(true);
// update GA consent to granted and send page_view
updateAnalyticsConsent('accepted');
};
const handleOptOut = () => {
// set consent status to opted-out so user can continue using app without analytics
setConsentStatus('opted-out');
setShowConsentDialog(false);
setHasConsent(true);
// update GA consent to denied and ensure no analytics are sent
updateAnalyticsConsent('opted-out');
};
if (!isTrulyVisible) {
// Render a minimal version or nothing while the browser is prerendering.
// This prevents heavy computations or API calls during the browser's prerender phase.
// console.log("Page is being prerendered by the browser (or support for detection is present). Waiting for activation.");
return <LoadingScreen />;
}
if (!hasConsent) {
return (
<>
<PrivacyConsentDialog
isOpen={showConsentDialog}
onAccept={handleAccept}
onOptOut={handleOptO
gitextract_dgsxokkm/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── 1-Bug_report.md
│ │ └── 3-Feature_request.md
│ ├── config.yml
│ ├── stale.yml
│ └── workflows/
│ └── release.yml
├── .gitignore
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── biome.json
├── build/
│ ├── entitlements.mac.plist
│ └── icon.icns
├── dev-app-update.yml
├── electron-builder.yml
├── electron.vite.config.ts
├── package.json
├── scripts/
│ ├── bump-version.js
│ └── undo-version-bump.js
├── src/
│ ├── client-viewer/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── public/
│ │ │ ├── img/
│ │ │ │ └── .gitkeep
│ │ │ ├── locales/
│ │ │ │ ├── da/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── de/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── en/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── es/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── fi/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── fr/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── it/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── ja/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── ko/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── nl/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── ru/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── sv/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── ua/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── zh_CN/
│ │ │ │ │ └── translation.json
│ │ │ │ └── zh_TW/
│ │ │ │ └── translation.json
│ │ │ ├── manifest.json
│ │ │ └── robots.txt
│ │ ├── scripts/
│ │ │ └── ga-interceptor.js
│ │ ├── src/
│ │ │ ├── App.css
│ │ │ ├── App.tsx
│ │ │ ├── api/
│ │ │ │ ├── config.ts
│ │ │ │ └── generator.ts
│ │ │ ├── assets/
│ │ │ │ ├── index.html
│ │ │ │ ├── locales/
│ │ │ │ │ ├── da/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── de/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── en/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── es/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── fi/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── fr/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── it/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── ja/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── ko/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── nl/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── ru/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── sv/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── ua/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ ├── zh_CN/
│ │ │ │ │ │ └── translation.json
│ │ │ │ │ └── zh_TW/
│ │ │ │ │ └── translation.json
│ │ │ │ ├── manifest.json
│ │ │ │ └── robots.txt
│ │ │ ├── components/
│ │ │ │ ├── ConnectingIndicator/
│ │ │ │ │ ├── ConnectingIndicatorIcon.tsx
│ │ │ │ │ ├── LoadingSharingIcon.tsx
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── ErrorDialog/
│ │ │ │ │ ├── ErrorMessageEnum.ts
│ │ │ │ │ ├── index.css
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── LoadingScreen/
│ │ │ │ │ ├── LoadingScreen.css
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── MyDeviceInfoCard/
│ │ │ │ │ ├── DeviceDetails.d.ts
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── PlayerControlPanel/
│ │ │ │ │ ├── handlePlayerToggleFullscreen.ts
│ │ │ │ │ ├── index.css
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── initScreenfullOnChange.ts
│ │ │ │ ├── PrivacyConsentDialog/
│ │ │ │ │ ├── index.css
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── PrivacyControlDialog/
│ │ │ │ │ ├── index.css
│ │ │ │ │ └── index.tsx
│ │ │ │ └── VideoJSPlayer/
│ │ │ │ ├── index.tsx
│ │ │ │ └── videojs-contain.css
│ │ │ ├── config/
│ │ │ │ └── i18n.ts
│ │ │ ├── constants/
│ │ │ │ ├── appConstants.ts
│ │ │ │ └── styleConstants.ts
│ │ │ ├── containers/
│ │ │ │ ├── ConnectionPrompts/
│ │ │ │ │ └── index.tsx
│ │ │ │ ├── MainView/
│ │ │ │ │ ├── ConnectionIconEnum.ts
│ │ │ │ │ ├── LoadingSharingIconEnum.ts
│ │ │ │ │ ├── changeLanguage.ts
│ │ │ │ │ ├── handleCreatePeerConnection.ts
│ │ │ │ │ ├── handleDisplayingLoadingSharingIconLoop.ts
│ │ │ │ │ ├── handleNoConnectionTimeout.ts
│ │ │ │ │ ├── handleRemoveDanglingReactRevealContainer.ts
│ │ │ │ │ ├── handleSetVideoQuality.ts
│ │ │ │ │ ├── index.css
│ │ │ │ │ ├── index.tsx
│ │ │ │ │ └── useScreenViewingTracker.ts
│ │ │ │ └── PlayerView/
│ │ │ │ └── index.tsx
│ │ │ ├── features/
│ │ │ │ ├── PeerConnection/
│ │ │ │ │ ├── NullUser.ts
│ │ │ │ │ ├── PartnerPeerUser.d.ts
│ │ │ │ │ ├── PeerConnection.d.ts
│ │ │ │ │ ├── PeerConnectionUIHandler.ts
│ │ │ │ │ ├── ReceiveEncryptedMessagePayload.d.ts
│ │ │ │ │ ├── ScreenSharingSourceEnum.ts
│ │ │ │ │ ├── errors/
│ │ │ │ │ │ ├── PeerConnectionPartnerIsNotDefinedError.ts
│ │ │ │ │ │ ├── PeerConnectionPeerIsNullError.ts
│ │ │ │ │ │ ├── PeerConnectionSocketNotDefined.ts
│ │ │ │ │ │ └── PeerConnectionUserIsNotDefinedError.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── mocks/
│ │ │ │ │ │ ├── INPUTtestWindowNavigatorUserAgent.ts
│ │ │ │ │ │ ├── INPUTvideo500000testSdpMediaBitrate.ts
│ │ │ │ │ │ ├── OUTPUTDeviceDetailsFromUAParsed.ts
│ │ │ │ │ │ └── OUTPUTvideo500000testSdpMediaBitrate.ts
│ │ │ │ │ ├── peerConnectionHandlePeer.ts
│ │ │ │ │ ├── peerConnectionHandleSocket.ts
│ │ │ │ │ ├── peerConnectionReceiveEncryptedMessage.ts
│ │ │ │ │ ├── setAndShowErrorDialogMessage.ts
│ │ │ │ │ ├── setSdpMediaBitrate.ts
│ │ │ │ │ ├── simplePeerDataMessages.ts
│ │ │ │ │ └── startSocketConnectedCheckingLoop/
│ │ │ │ │ └── index.ts
│ │ │ │ └── VideoAutoQualityOptimizer/
│ │ │ │ ├── VideoQualityEnum.ts
│ │ │ │ ├── errors/
│ │ │ │ │ ├── CanvasNotDefinedError.ts
│ │ │ │ │ ├── ImageDataIsUndefinedError.ts
│ │ │ │ │ ├── VideoDimensionsAreWrongError.ts
│ │ │ │ │ └── VideoNotDefinedError.ts
│ │ │ │ └── index.ts
│ │ │ ├── index.css
│ │ │ ├── main.tsx
│ │ │ ├── providers/
│ │ │ │ └── AppContextProvider/
│ │ │ │ └── index.tsx
│ │ │ ├── utils/
│ │ │ │ ├── ProcessedMessage.d.ts
│ │ │ │ ├── analytics.ts
│ │ │ │ ├── gaRequestInterceptor.ts
│ │ │ │ ├── message.ts
│ │ │ │ ├── playerFullscreen.ts
│ │ │ │ ├── socket.ts
│ │ │ │ └── userAgentParserHelpers.ts
│ │ │ └── vite-env.d.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.node.json
│ │ └── vite.config.ts
│ ├── common/
│ │ ├── DesktopCapturerSourceType.ts
│ │ ├── Device.ts
│ │ ├── ElectronStoreKeys.enum.ts
│ │ ├── IpcEvents.enum.ts
│ │ ├── LocalPeerUser.ts
│ │ ├── SendEncryptedMessagePayload.ts
│ │ ├── app.lang.config.ts
│ │ ├── config.ts
│ │ ├── connectSocket.ts
│ │ ├── deskreen-electron-store.ts
│ │ ├── getAppLanguage.ts
│ │ ├── isProduction.ts
│ │ ├── locales/
│ │ │ ├── da/
│ │ │ │ └── translation.json
│ │ │ ├── de/
│ │ │ │ └── translation.json
│ │ │ ├── en/
│ │ │ │ └── translation.json
│ │ │ ├── es/
│ │ │ │ └── translation.json
│ │ │ ├── fi/
│ │ │ │ └── translation.json
│ │ │ ├── fr/
│ │ │ │ └── translation.json
│ │ │ ├── it/
│ │ │ │ └── translation.json
│ │ │ ├── ja/
│ │ │ │ └── translation.json
│ │ │ ├── ko/
│ │ │ │ └── translation.json
│ │ │ ├── nl/
│ │ │ │ └── translation.json
│ │ │ ├── ru/
│ │ │ │ └── translation.json
│ │ │ ├── sv/
│ │ │ │ └── translation.json
│ │ │ ├── ua/
│ │ │ │ └── translation.json
│ │ │ ├── zh_CN/
│ │ │ │ └── translation.json
│ │ │ └── zh_TW/
│ │ │ └── translation.json
│ │ └── rateLimitedConsole.ts
│ ├── features/
│ │ ├── ConnectedDevicesService/
│ │ │ └── index.ts
│ │ ├── DesktopCapturerSourcesService/
│ │ │ └── index.ts
│ │ ├── PeerConnectionHelperRendererService/
│ │ │ └── index.ts
│ │ └── SharingSessionService/
│ │ ├── SharingSession.ts
│ │ ├── SharingSessionStatusEnum.ts
│ │ ├── SharingTypeEnum.ts
│ │ └── index.ts
│ ├── main/
│ │ ├── configs/
│ │ │ └── i18next.config.ts
│ │ ├── helpers/
│ │ │ ├── getDeskreenGlobal.ts
│ │ │ ├── getMyLocalIpV4.ts
│ │ │ ├── initGlobals.ts
│ │ │ ├── ipcMainHandlers.ts
│ │ │ └── isWifiConnected.ts
│ │ ├── index.ts
│ │ ├── menu.ts
│ │ └── utils/
│ │ ├── LoggerWithFilePrefix.ts
│ │ ├── getNewVersionTag.ts
│ │ ├── installExtensions.ts
│ │ └── isLinuxWaylandSession.ts
│ ├── preload/
│ │ ├── index.d.ts
│ │ └── index.ts
│ ├── renderer/
│ │ ├── index.html
│ │ ├── peerConnectionHelperRendererWindowIndex.html
│ │ └── src/
│ │ ├── assets/
│ │ │ ├── base.css
│ │ │ └── main.css
│ │ ├── components/
│ │ │ ├── AllowConnectionForDeviceAlert.tsx
│ │ │ ├── CloseOverlayButton.tsx
│ │ │ ├── ConnectedDevicesListDrawer.tsx
│ │ │ ├── DeviceInfoCallout/
│ │ │ │ └── index.tsx
│ │ │ ├── LanguageSelector/
│ │ │ │ └── index.tsx
│ │ │ ├── SettingsOverlay/
│ │ │ │ ├── SettingRowLabelAndInput.tsx
│ │ │ │ ├── SettingsOverlay.tsx
│ │ │ │ └── settings-overlay.css
│ │ │ ├── ShareAppOrScreenControlGroup.tsx
│ │ │ ├── SharingSourcePreviewCard/
│ │ │ │ └── index.tsx
│ │ │ ├── StepperPanel/
│ │ │ │ ├── ColorlibConnector.tsx
│ │ │ │ ├── ColorlibStepIcon.tsx
│ │ │ │ └── DeviceConnectedInfoButton.tsx
│ │ │ ├── StepsOfStepper/
│ │ │ │ ├── ChooseAppOrScreenOverlay/
│ │ │ │ │ ├── ChooseAppOrScreenOverlay.tsx
│ │ │ │ │ ├── PreviewGridList.tsx
│ │ │ │ │ └── ViewSharingObject.d.ts
│ │ │ │ ├── ChooseAppOrScreenStep.tsx
│ │ │ │ ├── ConfirmStep.tsx
│ │ │ │ ├── IntermediateStep.tsx
│ │ │ │ ├── ScanQRStep.tsx
│ │ │ │ └── SuccessStep.tsx
│ │ │ ├── TopPanel.tsx
│ │ │ └── css.d.ts
│ │ ├── configs/
│ │ │ └── i18next.config.client.ts
│ │ ├── containers/
│ │ │ ├── App.tsx
│ │ │ ├── DeskreenStepper.tsx
│ │ │ ├── HomePage.tsx
│ │ │ ├── Root.tsx
│ │ │ └── SettingsProvider.tsx
│ │ ├── contexts/
│ │ │ └── SettingsContext.tsx
│ │ ├── env.d.ts
│ │ ├── features/
│ │ │ └── PeerConnection/
│ │ │ ├── NullSimplePeer.ts
│ │ │ ├── NullUser.ts
│ │ │ ├── PartnerPeerUser.d.ts
│ │ │ ├── PeerConnection.d.ts
│ │ │ ├── ReceiveEncryptedMessagePayload.d.ts
│ │ │ ├── createDesktopCapturerStream.ts
│ │ │ ├── getDesktopSourceStreamBySourceID.ts
│ │ │ ├── handleCreatePeer.ts
│ │ │ ├── handlePeerOnData.ts
│ │ │ ├── handleSelfDestroy.ts
│ │ │ ├── handleSetDisplaySizeFromLocalStream.ts
│ │ │ ├── handleSocket.ts
│ │ │ ├── handleSocketUserEnter.ts
│ │ │ ├── handleSocketUserExit.ts
│ │ │ ├── index.ts
│ │ │ ├── prepareDataMessageToSendScreenSourceType.ts
│ │ │ ├── setSdpMediaBitrate.ts
│ │ │ └── simplePeerHandleSdpTransform.ts
│ │ ├── main.tsx
│ │ ├── peerConnectionHelperRendererWindowIndex.tsx
│ │ └── utils/
│ │ ├── handleRecieveEncryptedMessage.ts
│ │ ├── message.ts
│ │ └── showMessageFromNewToaster.ts
│ └── server/
│ ├── Room.d.ts
│ ├── RoomIDService/
│ │ └── index.ts
│ ├── darkwireSocket.ts
│ ├── getClientViewerDistPath.ts
│ ├── index.ts
│ ├── onDeviceConnectedCallback.ts
│ ├── socketsIPService.ts
│ ├── startPollForInactiveRooms.ts
│ └── store/
│ ├── MemoryStore.ts
│ ├── index.ts
│ └── socketIOServerStore.ts
├── tsconfig.json
├── tsconfig.node.json
├── tsconfig.web.json
└── wiki/
└── Home.md
SYMBOL INDEX (467 symbols across 126 files)
FILE: electron.vite.config.ts
method writeBundle (line 17) | async writeBundle() {
method writeBundle (line 42) | async writeBundle() {
FILE: scripts/bump-version.js
function parseArgs (line 35) | function parseArgs(argv) {
function main (line 50) | async function main({ type }) {
function ensureCleanGit (line 66) | async function ensureCleanGit() {
function readVersion (line 77) | async function readVersion() {
function writeRootPackage (line 88) | async function writeRootPackage(version) {
function writeClientPackage (line 92) | async function writeClientPackage(version) {
function updatePackageJSON (line 96) | async function updatePackageJSON(filePath, version) {
function writeEnv (line 104) | async function writeEnv(version) {
function bumpVersion (line 121) | function bumpVersion(current, type) {
function assertValidSemver (line 137) | function assertValidSemver(value) {
function stageFiles (line 144) | async function stageFiles() {
function commit (line 150) | async function commit(version) {
function tag (line 155) | async function tag(version) {
function quote (line 175) | function quote(value) {
FILE: scripts/undo-version-bump.js
function main (line 27) | async function main() {
function ensureCleanGit (line 69) | async function ensureCleanGit() {
function getLatestVersionTag (line 80) | async function getLatestVersionTag() {
function getTagCommit (line 97) | async function getTagCommit(tagName) {
function getParentCommit (line 107) | async function getParentCommit(commitHash) {
function getVersionFromCommit (line 119) | async function getVersionFromCommit(commitHash) {
function writeRootPackage (line 141) | async function writeRootPackage(version) {
function writeClientPackage (line 145) | async function writeClientPackage(version) {
function updatePackageJSON (line 149) | async function updatePackageJSON(filePath, version) {
function writeEnv (line 157) | async function writeEnv(version) {
function assertValidSemver (line 186) | function assertValidSemver(value) {
function deleteLocalTag (line 193) | async function deleteLocalTag(tagName) {
function deleteRemoteTag (line 209) | async function deleteRemoteTag(tagName) {
function quote (line 224) | function quote(value) {
FILE: src/client-viewer/scripts/ga-interceptor.js
function getConsentStatus (line 14) | function getConsentStatus() {
function isGoogleAnalyticsUrl (line 23) | function isGoogleAnalyticsUrl(url) {
function shouldBlockRequest (line 35) | function shouldBlockRequest() {
function isLocalIP (line 39) | function isLocalIP(ip) {
function sanitizeGAUrl (line 55) | function sanitizeGAUrl(url) {
FILE: src/client-viewer/src/components/ConnectingIndicator/ConnectingIndicatorIcon.tsx
type ConnectingIndicatorIconProps (line 4) | interface ConnectingIndicatorIconProps {
function ConnectingIndicatorIcon (line 8) | function ConnectingIndicatorIcon(props: ConnectingIndicatorIconProps) {
FILE: src/client-viewer/src/components/ConnectingIndicator/LoadingSharingIcon.tsx
type SelectSharingIconProps (line 5) | interface SelectSharingIconProps {
function LoadingSharingIcon (line 10) | function LoadingSharingIcon(props: SelectSharingIconProps) {
FILE: src/client-viewer/src/components/ConnectingIndicator/index.tsx
function getConnectingStepContent (line 19) | function getConnectingStepContent(
type ConnectingIndicatorProps (line 82) | interface ConnectingIndicatorProps {
function ConnectingIndicator (line 89) | function ConnectingIndicator(props: ConnectingIndicatorProps) {
FILE: src/client-viewer/src/components/ErrorDialog/ErrorMessageEnum.ts
type ErrorMessageType (line 9) | type ErrorMessageType = (typeof ErrorMessage)[keyof typeof ErrorMessage];
FILE: src/client-viewer/src/components/ErrorDialog/index.tsx
type ErrorDialogProps (line 7) | interface ErrorDialogProps {
function ErrorDialog (line 13) | function ErrorDialog(props: ErrorDialogProps) {
FILE: src/client-viewer/src/components/MyDeviceInfoCard/DeviceDetails.d.ts
type DeviceDetails (line 1) | interface DeviceDetails {
FILE: src/client-viewer/src/components/MyDeviceInfoCard/index.tsx
type MyDeviceDetailsCardProps (line 5) | interface MyDeviceDetailsCardProps {
function MyDeviceInfoCard (line 9) | function MyDeviceInfoCard(props: MyDeviceDetailsCardProps) {
FILE: src/client-viewer/src/components/PlayerControlPanel/index.tsx
type PlayerControlPanelProps (line 46) | interface PlayerControlPanelProps {
function PlayerControlPanel (line 58) | function PlayerControlPanel(props: PlayerControlPanelProps) {
FILE: src/client-viewer/src/components/PrivacyConsentDialog/index.tsx
type PrivacyConsentDialogProps (line 17) | interface PrivacyConsentDialogProps {
constant AVAILABLE_LANGUAGES (line 23) | const AVAILABLE_LANGUAGES = [
function TranslatedContent (line 41) | function TranslatedContent() {
function TranslatedButtons (line 112) | function TranslatedButtons({
function PrivacyConsentDialog (line 155) | function PrivacyConsentDialog(props: PrivacyConsentDialogProps) {
FILE: src/client-viewer/src/components/PrivacyControlDialog/index.tsx
type PrivacyControlDialogProps (line 18) | interface PrivacyControlDialogProps {
constant AVAILABLE_LANGUAGES (line 25) | const AVAILABLE_LANGUAGES = [
function TranslatedContent (line 43) | function TranslatedContent() {
function TranslatedButtons (line 114) | function TranslatedButtons({
function PrivacyControlDialog (line 167) | function PrivacyControlDialog(props: PrivacyControlDialogProps) {
FILE: src/client-viewer/src/components/VideoJSPlayer/index.tsx
type VideoJSPlayerProps (line 8) | interface VideoJSPlayerProps {
type VideoJsPlayerInstance (line 14) | type VideoJsPlayerInstance = {
function VideoJSPlayer (line 20) | function VideoJSPlayer(props: VideoJSPlayerProps) {
FILE: src/client-viewer/src/constants/appConstants.ts
constant PLAYER_WRAPPER_ID (line 1) | const PLAYER_WRAPPER_ID = 'player-wrapper-id';
constant VIDEO_QUALITY_TO_DECIMAL (line 2) | const VIDEO_QUALITY_TO_DECIMAL = {
constant COMPARISON_CANVAS_ID (line 9) | const COMPARISON_CANVAS_ID = 'comparison-canvas';
constant DUMMY_MY_DEVICE_DETAILS (line 10) | const DUMMY_MY_DEVICE_DETAILS = {
FILE: src/client-viewer/src/constants/styleConstants.ts
constant LIGHT_UI_BACKGROUND (line 1) | const LIGHT_UI_BACKGROUND = 'rgba(240, 248, 250, 1)';
FILE: src/client-viewer/src/containers/ConnectionPrompts/index.tsx
type ConnectionPropmptsProps (line 10) | interface ConnectionPropmptsProps {
function getPromptContent (line 19) | function getPromptContent(t: TFunction, step: number) {
function ConnectionPropmpts (line 48) | function ConnectionPropmpts(props: ConnectionPropmptsProps) {
FILE: src/client-viewer/src/containers/MainView/LoadingSharingIconEnum.ts
type LoadingSharingIconType (line 6) | type LoadingSharingIconType =
FILE: src/client-viewer/src/containers/MainView/index.tsx
function MainView (line 28) | function MainView() {
FILE: src/client-viewer/src/containers/MainView/useScreenViewingTracker.ts
type UseScreenViewingTrackerParams (line 5) | interface UseScreenViewingTrackerParams {
function formatErrorMessageForEvent (line 12) | function formatErrorMessageForEvent(errorMessage: ErrorMessageType): str...
function useScreenViewingTracker (line 21) | function useScreenViewingTracker(
FILE: src/client-viewer/src/containers/PlayerView/index.tsx
type PlayerViewProps (line 13) | interface PlayerViewProps {
type IOSVideoElement (line 25) | type IOSVideoElement = HTMLVideoElement & {
function PlayerView (line 32) | function PlayerView(props: PlayerViewProps) {
FILE: src/client-viewer/src/features/PeerConnection/PartnerPeerUser.d.ts
type PartnerPeerUser (line 1) | interface PartnerPeerUser {
FILE: src/client-viewer/src/features/PeerConnection/PeerConnection.d.ts
type PeerConnection (line 1) | type PeerConnection = import('.').default;
FILE: src/client-viewer/src/features/PeerConnection/PeerConnectionUIHandler.ts
class PeerConnectionUIHandler (line 6) | class PeerConnectionUIHandler {
method constructor (line 21) | constructor(
FILE: src/client-viewer/src/features/PeerConnection/ReceiveEncryptedMessagePayload.d.ts
type ReceiveEncryptedMessagePayload (line 1) | interface ReceiveEncryptedMessagePayload {
FILE: src/client-viewer/src/features/PeerConnection/ScreenSharingSourceEnum.ts
type ScreenSharingSourceType (line 6) | type ScreenSharingSourceType =
FILE: src/client-viewer/src/features/PeerConnection/errors/PeerConnectionPartnerIsNotDefinedError.ts
class PeerConnectionPartnerIsNotDefinedError (line 1) | class PeerConnectionPartnerIsNotDefinedError extends Error {
method constructor (line 2) | constructor() {
FILE: src/client-viewer/src/features/PeerConnection/errors/PeerConnectionPeerIsNullError.ts
class PeerConnectionPeerIsNullError (line 1) | class PeerConnectionPeerIsNullError extends Error {
method constructor (line 2) | constructor() {
FILE: src/client-viewer/src/features/PeerConnection/errors/PeerConnectionSocketNotDefined.ts
class PeerConnectionSocketNotDefined (line 1) | class PeerConnectionSocketNotDefined extends Error {
method constructor (line 2) | constructor() {
FILE: src/client-viewer/src/features/PeerConnection/errors/PeerConnectionUserIsNotDefinedError.ts
class PeerConnectionUserIsNotDefinedError (line 1) | class PeerConnectionUserIsNotDefinedError extends Error {
method constructor (line 2) | constructor() {
FILE: src/client-viewer/src/features/PeerConnection/index.ts
class PeerConnection (line 32) | class PeerConnection {
method constructor (line 75) | constructor(
method setVideoQuality (line 97) | setVideoQuality(videoQuality: VideoQualityType) {
method videoQualityChangedCallback (line 102) | videoQualityChangedCallback() {
method stopStream (line 115) | stopStream() {
method destroy (line 132) | destroy() {
method createPeer (line 182) | createPeer() {
method initApp (line 218) | initApp(user: LocalPeerUser, myIP: string) {
method createUser (line 228) | createUser() {
method sendEncryptedMessage (line 240) | sendEncryptedMessage(payload: SendEncryptedMessagePayload) {
method receiveEncryptedMessage (line 257) | receiveEncryptedMessage(payload: ReceiveEncryptedMessagePayload) {
method createUserAndInitSocket (line 261) | createUserAndInitSocket() {
FILE: src/client-viewer/src/features/PeerConnection/peerConnectionHandlePeer.ts
function getSharingShourceType (line 10) | function getSharingShourceType(peerConnection: PeerConnection) {
FILE: src/client-viewer/src/features/PeerConnection/peerConnectionHandleSocket.ts
function getMyIPCallback (line 10) | function getMyIPCallback(
FILE: src/client-viewer/src/features/PeerConnection/simplePeerDataMessages.ts
function prepareDataMessageToChangeQuality (line 1) | function prepareDataMessageToChangeQuality(q: number) {
function prepareDataMessageToGetSharingSourceType (line 12) | function prepareDataMessageToGetSharingSourceType() {
FILE: src/client-viewer/src/features/VideoAutoQualityOptimizer/VideoQualityEnum.ts
type VideoQualityType (line 10) | type VideoQualityType = (typeof VideoQuality)[keyof typeof VideoQuality];
FILE: src/client-viewer/src/features/VideoAutoQualityOptimizer/errors/CanvasNotDefinedError.ts
class CanvasNotDefinedError (line 1) | class CanvasNotDefinedError extends Error {
method constructor (line 2) | constructor() {
FILE: src/client-viewer/src/features/VideoAutoQualityOptimizer/errors/ImageDataIsUndefinedError.ts
class ImageDataIsUndefinedError (line 1) | class ImageDataIsUndefinedError extends Error {
method constructor (line 2) | constructor() {
FILE: src/client-viewer/src/features/VideoAutoQualityOptimizer/errors/VideoDimensionsAreWrongError.ts
class VideoDimensionsAreWrongError (line 1) | class VideoDimensionsAreWrongError extends Error {
method constructor (line 2) | constructor() {
FILE: src/client-viewer/src/features/VideoAutoQualityOptimizer/errors/VideoNotDefinedError.ts
class VideoNotDefinedError (line 1) | class VideoNotDefinedError extends Error {
method constructor (line 2) | constructor() {
FILE: src/client-viewer/src/features/VideoAutoQualityOptimizer/index.ts
constant CANVAS_SCALE_MULTIPLIER (line 9) | const CANVAS_SCALE_MULTIPLIER = 0.125;
constant MISMATCH_PERCENT_THRESHOLD (line 10) | const MISMATCH_PERCENT_THRESHOLD = 0.1;
class VideoAutoQualityOptimizer (line 12) | class VideoAutoQualityOptimizer {
method setGoodQualityCallback (line 31) | setGoodQualityCallback(callback: () => void) {
method setHalfQualityCallbak (line 35) | setHalfQualityCallbak(callback: () => void) {
method startOptimizationLoop (line 39) | startOptimizationLoop() {
method doFrameComparisonAndQualityOptimization (line 53) | doFrameComparisonAndQualityOptimization() {
method findAndSetVideoInternalVariable (line 81) | findAndSetVideoInternalVariable(document: Document) {
method findAndSetCanvasInternalVariable (line 87) | findAndSetCanvasInternalVariable(document: Document) {
method prepareCanvasAndVideo (line 93) | prepareCanvasAndVideo() {
method clearCanvas (line 100) | clearCanvas() {
method validateVideoWidthAndHeight (line 106) | validateVideoWidthAndHeight() {
method validateBeforeCalculations (line 112) | validateBeforeCalculations() {
method validateVideoIsDefined (line 118) | validateVideoIsDefined() {
method validateCanvasIsDefined (line 124) | validateCanvasIsDefined() {
method scaleCanvas (line 130) | scaleCanvas() {
method drawVideoFrameToCanvas (line 136) | drawVideoFrameToCanvas() {
method getImageDataFromCanvas (line 143) | getImageDataFromCanvas() {
method getNumberOfMismatchedPixels (line 149) | getNumberOfMismatchedPixels(imageData: ImageData) {
method getPreviousAndCurrentFrameMismatchInPercent (line 161) | getPreviousAndCurrentFrameMismatchInPercent(imageData: ImageData) {
method handleFramesMismatch (line 169) | handleFramesMismatch(mismatchInPercent: number) {
method isLowMismatchPercent (line 186) | isLowMismatchPercent(mismatchInPercent: number) {
method isHighMismatchPercent (line 190) | isHighMismatchPercent(mismatchInPercent: number) {
FILE: src/client-viewer/src/providers/AppContextProvider/index.tsx
type AppContextInterface (line 3) | interface AppContextInterface {
FILE: src/client-viewer/src/utils/ProcessedMessage.d.ts
type CallUserMessageWithPayload (line 1) | type CallUserMessageWithPayload = {
type DeviceDetailsMessageWithPayload (line 8) | type DeviceDetailsMessageWithPayload = {
type DenyToConnectMessageWithPayload (line 20) | type DenyToConnectMessageWithPayload = {
type DisconnectByHostMachineUserMessageWithPayload (line 25) | type DisconnectByHostMachineUserMessageWithPayload = {
type AllowedToConnectMessageWithPayload (line 30) | type AllowedToConnectMessageWithPayload = {
type AppLanguageMessageWithPayload (line 35) | type AppLanguageMessageWithPayload = {
type ProcessedMessage (line 42) | type ProcessedMessage =
FILE: src/client-viewer/src/utils/analytics.ts
type Window (line 3) | interface Window {
constant CONSENT_KEY (line 9) | const CONSENT_KEY = 'deskreen_ga_consent';
constant GA_TAG_PLACEHOLDER (line 10) | const GA_TAG_PLACEHOLDER = '%VITE_CLIENT_VIEWER_GA_TAG%';
constant CLIENT_VIEWER_VERSION_PLACEHOLDER (line 11) | const CLIENT_VIEWER_VERSION_PLACEHOLDER = '%VITE_CLIENT_VIEWER_VERSION%';
type AnalyticsEventParams (line 15) | type AnalyticsEventParams = Record<string, string | number | boolean>;
type ConsentStatus (line 17) | type ConsentStatus = 'accepted' | 'opted-out' | null;
function getConsentStatus (line 19) | function getConsentStatus(): ConsentStatus {
function setConsentStatus (line 31) | function setConsentStatus(status: 'accepted' | 'opted-out'): void {
function clearConsentStatus (line 38) | function clearConsentStatus(): void {
function loadGoogleAnalytics (line 45) | function loadGoogleAnalytics(gaTagId: string): void {
function sendPageView (line 128) | function sendPageView(): void {
function waitForGAReady (line 143) | function waitForGAReady(callback: () => void): void {
function updateAnalyticsConsent (line 182) | function updateAnalyticsConsent(consentStatus: ConsentStatus): void {
function getGaTagIdFromMeta (line 222) | function getGaTagIdFromMeta(): string | null {
function getClientViewerVersion (line 245) | function getClientViewerVersion(): string | null {
function sendClientViewerVersionEvent (line 263) | function sendClientViewerVersionEvent(): void {
function trackAnalyticsEvent (line 279) | function trackAnalyticsEvent(
FILE: src/client-viewer/src/utils/gaRequestInterceptor.ts
constant GA_DOMAINS (line 3) | const GA_DOMAINS = [
function isGoogleAnalyticsUrl (line 30) | function isGoogleAnalyticsUrl(url: string): boolean {
function shouldBlockRequest (line 42) | function shouldBlockRequest(): boolean {
function isLocalIP (line 47) | function isLocalIP(ip: string): boolean {
function sanitizeGAUrl (line 76) | function sanitizeGAUrl(url: string): string {
function interceptFetch (line 114) | function interceptFetch(): void {
function interceptXMLHttpRequest (line 151) | function interceptXMLHttpRequest(): void {
function interceptSendBeacon (line 199) | function interceptSendBeacon(): void {
function initializeGARequestInterceptor (line 229) | function initializeGARequestInterceptor(): void {
FILE: src/client-viewer/src/utils/message.ts
type ProcessedPayload (line 4) | interface ProcessedPayload {
FILE: src/client-viewer/src/utils/playerFullscreen.ts
type LegacyFullscreenElement (line 4) | type LegacyFullscreenElement = HTMLElement & {
type LegacyFullscreenDocument (line 11) | type LegacyFullscreenDocument = Document & {
type IOSVideoElement (line 20) | type IOSVideoElement = HTMLVideoElement & {
type Unsubscribe (line 27) | type Unsubscribe = () => void;
FILE: src/client-viewer/src/utils/userAgentParserHelpers.ts
function getOSFromUAParser (line 4) | function getOSFromUAParser(uaParser: UAParser) {
function getDeviceTypeFromUAParser (line 12) | function getDeviceTypeFromUAParser(uaParser: UAParser) {
function getBrowserFromUAParser (line 20) | function getBrowserFromUAParser(uaParser: UAParser) {
FILE: src/client-viewer/src/vite-env.d.ts
type ConnectionIconType (line 2) | type ConnectionIconType =
type LoadingSharingIconType (line 5) | type LoadingSharingIconType =
type ScreenSharingSourceType (line 8) | type ScreenSharingSourceType =
type CreatePeerConnectionUseEffectParams (line 11) | type CreatePeerConnectionUseEffectParams = {
type handleDisplayingLoadingSharingIconLoopParams (line 24) | type handleDisplayingLoadingSharingIconLoopParams = {
type Document (line 33) | interface Document {
FILE: src/client-viewer/vite.config.ts
type PackageJson (line 10) | interface PackageJson {
method transformIndexHtml (line 33) | transformIndexHtml(html) {
FILE: src/common/DesktopCapturerSourceType.ts
type DesktopCapturerSourceType (line 1) | enum DesktopCapturerSourceType {
FILE: src/common/Device.ts
type Device (line 1) | interface Device {
FILE: src/common/ElectronStoreKeys.enum.ts
type ElectronStoreKeys (line 1) | enum ElectronStoreKeys {
FILE: src/common/IpcEvents.enum.ts
type IpcEvents (line 1) | enum IpcEvents {
FILE: src/common/LocalPeerUser.ts
type LocalPeerUser (line 1) | interface LocalPeerUser {
FILE: src/common/SendEncryptedMessagePayload.ts
type SendEncryptedMessagePayload (line 1) | interface SendEncryptedMessagePayload {
FILE: src/common/deskreen-electron-store.ts
type DataStore (line 19) | type DataStore = Record<string, string>;
class PersistentStorage (line 36) | class PersistentStorage {
method constructor (line 43) | constructor(fileName = 'deskreen-ce-storage.json') {
method writeData (line 52) | private writeData(): void {
method has (line 68) | public has(key: string): boolean {
method get (line 77) | public get(key: string): string | undefined {
method set (line 86) | public set(key: string, value: string): void {
method delete (line 95) | public delete(key: string): void {
method clear (line 103) | public clear(): void {
FILE: src/common/getAppLanguage.ts
function getAppLanguage (line 3) | async function getAppLanguage(): Promise<string> {
FILE: src/common/isProduction.ts
function isProduction (line 1) | function isProduction(): boolean {
FILE: src/common/rateLimitedConsole.ts
type LogEntry (line 4) | interface LogEntry {
constant RATE_LIMIT_WINDOW (line 11) | const RATE_LIMIT_WINDOW = 5000;
constant MAX_LOGS_PER_WINDOW (line 12) | const MAX_LOGS_PER_WINDOW = 10;
constant CACHE_CLEANUP_INTERVAL (line 13) | const CACHE_CLEANUP_INTERVAL = 30000;
function getLogKey (line 24) | function getLogKey(...args: unknown[]): string {
function shouldLog (line 44) | function shouldLog(key: string): boolean {
function cleanupCache (line 80) | function cleanupCache(): void {
function startConsoleRateLimiting (line 124) | function startConsoleRateLimiting(): void {
function stopConsoleRateLimiting (line 134) | function stopConsoleRateLimiting(): void {
function overrideGlobalConsole (line 143) | function overrideGlobalConsole(): void {
FILE: src/features/ConnectedDevicesService/index.ts
constant SLOT_VIOLATION_MESSAGE (line 15) | const SLOT_VIOLATION_MESSAGE = 'single viewer slot is already occupied';
type ViewerConnectionAvailability (line 17) | type ViewerConnectionAvailability = 'available' | 'occupied';
class SingleViewerSlot (line 19) | class SingleViewerSlot {
method occupy (line 22) | occupy(device: Device): void {
method releaseById (line 29) | releaseById(deviceIDToRemove: string): boolean {
method release (line 38) | release(): void {
method isAvailable (line 42) | isAvailable(): boolean {
method snapshot (line 46) | snapshot(): Device[] {
method isOccupiedBy (line 51) | isOccupiedBy(deviceID: string): boolean {
class ConnectedDevicesService (line 57) | class ConnectedDevicesService {
method resetPendingConnectionDevice (line 66) | resetPendingConnectionDevice(): void {
method getDevices (line 70) | getDevices(): Device[] {
method isSlotAvailable (line 74) | isSlotAvailable(): boolean {
method addAvailabilityListener (line 78) | addAvailabilityListener(
method disconnectAllDevices (line 88) | disconnectAllDevices(): void {
method disconnectDeviceByID (line 93) | disconnectDeviceByID(deviceIDToRemove: string): Promise<undefined> {
method addDevice (line 101) | addDevice(device: Device): void {
method setPendingConnectionDevice (line 113) | setPendingConnectionDevice(device: Device): void {
method getAvailabilityState (line 117) | private getAvailabilityState(): ViewerConnectionAvailability {
method notifyAvailabilityListeners (line 121) | private notifyAvailabilityListeners(): void {
FILE: src/features/DesktopCapturerSourcesService/index.ts
type DesktopCapturerSourceWithType (line 9) | interface DesktopCapturerSourceWithType {
function getSourceTypeFromSourceID (line 14) | function getSourceTypeFromSourceID(
type SourcesDisappearListener (line 23) | type SourcesDisappearListener = (ids: string[]) => void;
type SharingSessionID (line 24) | type SharingSessionID = string;
class DesktopCapturerSourcesService (line 26) | class DesktopCapturerSourcesService {
method constructor (line 48) | constructor() {
method getSourcesMap (line 74) | getSourcesMap(): Map<string, DesktopCapturerSourceWithType> {
method startRefreshDesktopCapturerSourcesLoop (line 78) | startRefreshDesktopCapturerSourcesLoop(): void {
method getScreenSources (line 87) | getScreenSources(): DesktopCapturerSource[] {
method getAppWindowSources (line 99) | getAppWindowSources(): DesktopCapturerSource[] {
method getSourceDisplayIDByDisplayCapturerSourceID (line 111) | getSourceDisplayIDByDisplayCapturerSourceID(sourceID: string): string {
method addWindowClosedListener (line 123) | addWindowClosedListener(
method addScreenDisconnectedListener (line 130) | addScreenDisconnectedListener(
method updateDesktopCapturerSources (line 137) | async updateDesktopCapturerSources(): Promise<void> {
method getDesktopCapturerSources (line 156) | async getDesktopCapturerSources(): Promise<
method refreshDesktopCapturerSources (line 177) | async refreshDesktopCapturerSources(): Promise<void> {
method requestPortalSource (line 199) | async requestPortalSource(
method startPollForInactiveListenersLoop (line 243) | startPollForInactiveListenersLoop(): void {
method checkForClosedWindows (line 253) | checkForClosedWindows(): void {
method notifyOnWindowsClosedListeners (line 262) | notifyOnWindowsClosedListeners(_closedWindowsIDs: string[]): void {
method checkForScreensDisconnected (line 266) | checkForScreensDisconnected(): void {
method notifyOnScreensDisconnectedListeners (line 275) | notifyOnScreensDisconnectedListeners(
FILE: src/features/PeerConnectionHelperRendererService/index.ts
type RendererHelperWebcontentsID (line 5) | type RendererHelperWebcontentsID = number;
class RendererWebrtcHelpersService (line 7) | class RendererWebrtcHelpersService {
method constructor (line 11) | constructor(_appPath: string) {
method createPeerConnectionHelperRenderer (line 16) | createPeerConnectionHelperRenderer(): BrowserWindow {
FILE: src/features/SharingSessionService/SharingSession.ts
type SharingSessionStatusChangeListener (line 9) | type SharingSessionStatusChangeListener = (
class SharingSession (line 13) | class SharingSession {
method constructor (line 27) | constructor(
method destroy (line 84) | destroy(): void {
method setOnDeviceConnectedCallback (line 88) | setOnDeviceConnectedCallback(callback: (device: Device) => void): void {
method setDesktopCapturerSourceID (line 92) | setDesktopCapturerSourceID(id: string): void {
method callPeer (line 101) | callPeer(): void {
method disconnectByHostMachineUser (line 106) | disconnectByHostMachineUser(): void {
method denyConnectionForPartner (line 113) | denyConnectionForPartner(): void {
method appLanguageChanged (line 119) | appLanguageChanged(): void {
method addStatusChangeListener (line 123) | addStatusChangeListener(callback: SharingSessionStatusChangeListener):...
method notifyStatusChangeListeners (line 127) | notifyStatusChangeListeners(): Promise<undefined> {
method setStatus (line 136) | setStatus(newStatus: SharingSessionStatusEnum): void {
method setDeviceID (line 141) | setDeviceID(deviceID: string): void {
FILE: src/features/SharingSessionService/SharingSessionStatusEnum.ts
type SharingSessionStatusEnum (line 1) | enum SharingSessionStatusEnum {
FILE: src/features/SharingSessionService/SharingTypeEnum.ts
type SharingTypeEnum (line 1) | enum SharingTypeEnum {
FILE: src/features/SharingSessionService/index.ts
class SharingSessionService (line 9) | class SharingSessionService {
method constructor (line 18) | constructor(
method createUser (line 40) | createUser(): Promise<undefined> {
method createWaitingForConnectionSharingSession (line 55) | createWaitingForConnectionSharingSession(
method createNewSharingSession (line 109) | async createNewSharingSession(_roomID: string): Promise<SharingSession> {
method pollForInactiveSessions (line 122) | pollForInactiveSessions(): void {
method waitWhileUserIsNotCreated (line 136) | waitWhileUserIsNotCreated(): Promise<undefined> {
FILE: src/main/helpers/getMyLocalIpV4.ts
function isWifiInterface (line 74) | function isWifiInterface(interfaceName: string): boolean {
function isVirtualInterface (line 103) | function isVirtualInterface(interfaceName: string, interfaceMacAddress: ...
function getActiveNetworkInterface (line 141) | function getActiveNetworkInterface(): { interfaceName: string; ipAddress...
function getMyLocalIpV4 (line 192) | function getMyLocalIpV4(): string | undefined {
FILE: src/main/helpers/initGlobals.ts
type DeskreenGlobal (line 9) | interface DeskreenGlobal {
FILE: src/main/helpers/ipcMainHandlers.ts
function createWaitingForConnectionSharingSession (line 147) | async function createWaitingForConnectionSharingSession(
function resetWaitingForConnectionSharingSession (line 175) | function resetWaitingForConnectionSharingSession(): void {
function startSharingOnWaitingForConnectionSharingSession (line 352) | function startSharingOnWaitingForConnectionSharingSession(): void {
FILE: src/main/helpers/isWifiConnected.ts
function isWifiConnected (line 4) | function isWifiConnected(): boolean {
FILE: src/main/index.ts
class DeskreenApp (line 99) | class DeskreenApp {
method initElectronAppObject (line 106) | initElectronAppObject(): void {
method checkForLatestVersionAndNotify (line 153) | private async checkForLatestVersionAndNotify(): Promise<void> {
method showUpdateNotification (line 174) | private showUpdateNotification(latestAppVersion: string): void {
method createWindow (line 190) | async createWindow(): Promise<void> {
method initI18n (line 269) | initI18n(): void {
method start (line 291) | start(): void {
method parseCliLocalIp (line 319) | private parseCliLocalIp(): string | undefined {
FILE: src/main/menu.ts
type DarwinMenuItemConstructorOptions (line 11) | interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOp...
class MenuBuilder (line 16) | class MenuBuilder {
method constructor (line 21) | constructor(mainWindow: BrowserWindow, i18n: typeof i18nType) {
method buildMenu (line 26) | buildMenu(): void {
method setupDevelopmentEnvironment (line 43) | setupDevelopmentEnvironment(): void {
method buildDarwinTemplate (line 58) | buildDarwinTemplate(): MenuItemConstructorOptions[] {
FILE: src/main/utils/LoggerWithFilePrefix.ts
function cleanupLogBuffers (line 21) | function cleanupLogBuffers(): void {
function startLogBufferCleanup (line 39) | function startLogBufferCleanup(): void {
function stopLogBufferCleanup (line 53) | function stopLogBufferCleanup(): void {
class LoggerWithFilePrefix (line 60) | class LoggerWithFilePrefix {
method constructor (line 65) | constructor(_filenamePath: string) {
method error (line 70) | error(...args: unknown[]): void {
method warn (line 74) | warn(...args: unknown[]): void {
method info (line 78) | info(...args: unknown[]): void {
method verbose (line 82) | verbose(...args: unknown[]): void {
method debug (line 86) | debug(...args: unknown[]): void {
method silly (line 90) | silly(...args: unknown[]): void {
FILE: src/main/utils/getNewVersionTag.ts
function getNewVersionTag (line 6) | async function getNewVersionTag(): Promise<string> {
FILE: src/main/utils/installExtensions.ts
function installExtensions (line 6) | async function installExtensions(): Promise<void> {
FILE: src/preload/index.d.ts
type Window (line 5) | interface Window {
FILE: src/renderer/src/components/AllowConnectionForDeviceAlert.tsx
type AllowConnectionForDeviceAlertProps (line 7) | interface AllowConnectionForDeviceAlertProps {
FILE: src/renderer/src/components/CloseOverlayButton.tsx
class CloseOverlayButtonProps (line 5) | class CloseOverlayButtonProps {
FILE: src/renderer/src/components/ConnectedDevicesListDrawer.tsx
type DeviceWithDesktopCapturerSourceId (line 22) | type DeviceWithDesktopCapturerSourceId = Device & {
type ConnectedDevicesListDrawerProps (line 26) | interface ConnectedDevicesListDrawerProps {
function ConnectedDevicesListDrawer (line 48) | function ConnectedDevicesListDrawer(
FILE: src/renderer/src/components/DeviceInfoCallout/index.tsx
function getContentOfTooltip (line 7) | function getContentOfTooltip(t: TFunction) {
type DeviceInfoCalloutProps (line 22) | interface DeviceInfoCalloutProps {
FILE: src/renderer/src/components/LanguageSelector/index.tsx
function LanguageSelector (line 11) | function LanguageSelector() {
FILE: src/renderer/src/components/SettingsOverlay/SettingRowLabelAndInput.tsx
type SettingRowLabelAndInput (line 23) | interface SettingRowLabelAndInput {
function SettingRowLabelAndInput (line 29) | function SettingRowLabelAndInput(
FILE: src/renderer/src/components/SettingsOverlay/SettingsOverlay.tsx
type SettingsOverlayProps (line 23) | interface SettingsOverlayProps {
type SettingsOverlayClassKey (line 28) | type SettingsOverlayClassKey =
type SettingsOverlayClassMap (line 38) | type SettingsOverlayClassMap = Record<SettingsOverlayClassKey, string>;
function SettingsOverlay (line 74) | function SettingsOverlay(
FILE: src/renderer/src/components/ShareAppOrScreenControlGroup.tsx
type ShareAppOrScreenControlGroupProps (line 8) | interface ShareAppOrScreenControlGroupProps {
function ShareAppOrScreenControlGroup (line 55) | function ShareAppOrScreenControlGroup(
FILE: src/renderer/src/components/SharingSourcePreviewCard/index.tsx
class SharingSourcePreviewCardProps (line 7) | class SharingSourcePreviewCardProps {
FILE: src/renderer/src/components/StepperPanel/ColorlibStepIcon.tsx
type StepIconPropsDeskreen (line 7) | interface StepIconPropsDeskreen extends StepIconProps {
function ColorlibStepIcon (line 43) | function ColorlibStepIcon(
FILE: src/renderer/src/components/StepperPanel/DeviceConnectedInfoButton.tsx
type DeviceConnectedInfoButtonProps (line 8) | interface DeviceConnectedInfoButtonProps {
function DeviceConnectedInfoButton (line 51) | function DeviceConnectedInfoButton(
FILE: src/renderer/src/components/StepsOfStepper/ChooseAppOrScreenOverlay/ChooseAppOrScreenOverlay.tsx
type ChooseAppOrScreenOverlayProps (line 34) | interface ChooseAppOrScreenOverlayProps {
function ChooseAppOrScreenOverlay (line 43) | function ChooseAppOrScreenOverlay(
FILE: src/renderer/src/components/StepsOfStepper/ChooseAppOrScreenOverlay/PreviewGridList.tsx
type PreviewGridListProps (line 5) | interface PreviewGridListProps {
function PreviewGridList (line 12) | function PreviewGridList(props: PreviewGridListProps) {
FILE: src/renderer/src/components/StepsOfStepper/ChooseAppOrScreenOverlay/ViewSharingObject.d.ts
type ViewSharingObject (line 1) | type ViewSharingObject = { thumbnailUrl: string; name: string };
FILE: src/renderer/src/components/StepsOfStepper/ChooseAppOrScreenStep.tsx
type ChooseAppOrScreeenStepProps (line 5) | interface ChooseAppOrScreeenStepProps {
FILE: src/renderer/src/components/StepsOfStepper/ConfirmStep.tsx
type ConfirmStepProps (line 10) | interface ConfirmStepProps {
function ConfirmStep (line 14) | function ConfirmStep(props: ConfirmStepProps) {
FILE: src/renderer/src/components/StepsOfStepper/IntermediateStep.tsx
type IntermediateStepProps (line 11) | interface IntermediateStepProps {
function getStepContent (line 23) | function getStepContent(
function isConfirmStep (line 56) | function isConfirmStep(activeStep: number, steps: string[]): boolean {
function IntermediateStep (line 60) | function IntermediateStep(
FILE: src/renderer/src/components/StepsOfStepper/SuccessStep.tsx
type SuccessStepProps (line 6) | interface SuccessStepProps {
FILE: src/renderer/src/components/TopPanel.tsx
type Props (line 131) | interface Props {
function TopPanel (line 135) | function TopPanel({ handleReset }: Props): React.ReactElement {
FILE: src/renderer/src/configs/i18next.config.client.ts
function shuffleArray (line 47) | function shuffleArray(array: unknown[]): void {
function initI18NextOptions (line 81) | async function initI18NextOptions(): Promise<void> {
FILE: src/renderer/src/containers/App.tsx
type Props (line 3) | type Props = {
FILE: src/renderer/src/containers/DeskreenStepper.tsx
function getSteps (line 52) | function getSteps(t: TFunction): string[] {
type Props (line 56) | interface Props {
function stepperOpenedCallback (line 92) | async function stepperOpenedCallback(): Promise<void> {
FILE: src/renderer/src/containers/HomePage.tsx
function HomePage (line 32) | function HomePage(): React.ReactElement {
FILE: src/renderer/src/containers/SettingsProvider.tsx
constant LIGHT_UI_BACKGROUND (line 5) | const LIGHT_UI_BACKGROUND = 'rgba(240, 248, 250, 1)';
FILE: src/renderer/src/contexts/SettingsContext.tsx
type SettingsContextInterface (line 3) | interface SettingsContextInterface {
FILE: src/renderer/src/features/PeerConnection/PartnerPeerUser.d.ts
type PartnerPeerUser (line 1) | interface PartnerPeerUser {
FILE: src/renderer/src/features/PeerConnection/PeerConnection.d.ts
type PeerConnection (line 3) | type PeerConnection = import('./index').default;
FILE: src/renderer/src/features/PeerConnection/ReceiveEncryptedMessagePayload.d.ts
type ReceiveEncryptedMessagePayload (line 1) | interface ReceiveEncryptedMessagePayload {
FILE: src/renderer/src/features/PeerConnection/createDesktopCapturerStream.ts
function createDesktopCapturerStream (line 4) | async function createDesktopCapturerStream(
FILE: src/renderer/src/features/PeerConnection/getDesktopSourceStreamBySourceID.ts
function getStreamWithSource (line 1) | async function getStreamWithSource(
FILE: src/renderer/src/features/PeerConnection/handleCreatePeer.ts
function handleCreatePeer (line 7) | function handleCreatePeer(
FILE: src/renderer/src/features/PeerConnection/handlePeerOnData.ts
function handlePeerOnData (line 6) | async function handlePeerOnData(
FILE: src/renderer/src/features/PeerConnection/handleSelfDestroy.ts
function handleSelfDestroy (line 5) | function handleSelfDestroy(
FILE: src/renderer/src/features/PeerConnection/handleSetDisplaySizeFromLocalStream.ts
function setDisplaySizeFromLocalStream (line 1) | function setDisplaySizeFromLocalStream(
FILE: src/renderer/src/features/PeerConnection/handleSocket.ts
function handleSocket (line 4) | function handleSocket(peerConnection: PeerConnection): void {
FILE: src/renderer/src/features/PeerConnection/index.ts
type DisplaySize (line 20) | type DisplaySize = { width: number; height: number };
type PartnerPeerUser (line 22) | interface PartnerPeerUser {
class PeerConnection (line 26) | class PeerConnection {
method constructor (line 53) | constructor(
method notifyClientWithNewLanguage (line 83) | notifyClientWithNewLanguage(): void {
method setDesktopCapturerSourceID (line 94) | async setDesktopCapturerSourceID(id: string): Promise<void> {
method setDisplayIDByDesktopCapturerSourceID (line 107) | async setDisplayIDByDesktopCapturerSourceID(): Promise<void> {
method setDisplaySizeRetreivedFromMainProcess (line 127) | async setDisplaySizeRetreivedFromMainProcess(): Promise<void> {
method handleCreatePeerAfterDesktopCapturerSourceIDWasSet (line 138) | async handleCreatePeerAfterDesktopCapturerSourceIDWasSet(): Promise<vo...
method setOnDeviceConnectedCallback (line 218) | setOnDeviceConnectedCallback(callback: (device: Device) => void): void {
method denyConnectionForPartner (line 222) | async denyConnectionForPartner(): Promise<void> {
method sendUserAllowedToConnect (line 230) | sendUserAllowedToConnect(): void {
method disconnectByHostMachineUser (line 237) | async disconnectByHostMachineUser(deviceId: string): Promise<void> {
method disconnectPartner (line 249) | disconnectPartner(): void {
method selfDestroy (line 257) | selfDestroy(): void {
method emitUserEnter (line 261) | emitUserEnter(): void {
method sendEncryptedMessage (line 267) | async sendEncryptedMessage(
method receiveEncryptedMessage (line 278) | receiveEncryptedMessage(payload: ReceiveEncryptedMessagePayload): void {
method callPeer (line 283) | callPeer(): void {
method createPeer (line 298) | createPeer(): Promise<void> {
method toggleLockRoom (line 302) | toggleLockRoom(isConnected: boolean): void {
FILE: src/renderer/src/peerConnectionHelperRendererWindowIndex.tsx
function handleIpcRenderer (line 33) | function handleIpcRenderer(): void {
FILE: src/renderer/src/utils/handleRecieveEncryptedMessage.ts
type CallAcceptedMessageWithPayload (line 4) | type CallAcceptedMessageWithPayload = {
type CallUserMessageWithPayload (line 11) | type CallUserMessageWithPayload = {
type DeviceDetailsMessageWithPayload (line 18) | type DeviceDetailsMessageWithPayload = {
type GetAppLanguageMessageWithPayload (line 29) | type GetAppLanguageMessageWithPayload = {
type AppLanguageMessageWithPayload (line 34) | type AppLanguageMessageWithPayload = {
type DenyToConnectMessageWithPayload (line 41) | type DenyToConnectMessageWithPayload = {
type AllowedToConnectMessageWithPayload (line 46) | type AllowedToConnectMessageWithPayload = {
type DisconnectByHostMachineUserMessageWithPayload (line 51) | type DisconnectByHostMachineUserMessageWithPayload = {
type ProcessedMessage (line 56) | type ProcessedMessage =
function handleDeviceIPMessage (line 66) | function handleDeviceIPMessage(
FILE: src/renderer/src/utils/message.ts
type ProcessedPayload (line 5) | interface ProcessedPayload {
FILE: src/renderer/src/utils/showMessageFromNewToaster.ts
function showMessageFromNewToaster (line 4) | async function showMessageFromNewToaster(
FILE: src/server/Room.d.ts
type Room (line 1) | interface Room {
FILE: src/server/RoomIDService/index.ts
class RoomIDService (line 4) | class RoomIDService {
method constructor (line 9) | constructor() {
method getSimpleAvailableRoomID (line 15) | getSimpleAvailableRoomID(): Promise<string> {
method getShortIDStringOfAvailableRoom (line 31) | getShortIDStringOfAvailableRoom(): Promise<string> {
method markRoomIDAsTaken (line 41) | markRoomIDAsTaken(id: string): void {
method unmarkRoomIDAsTaken (line 45) | unmarkRoomIDAsTaken(id: string): void {
method isRoomIDTaken (line 49) | isRoomIDTaken(id: string): boolean {
FILE: src/server/darkwireSocket.ts
constant LOCALHOST_SOCKET_IP (line 13) | const LOCALHOST_SOCKET_IP = '127.0.0.1';
type SocketOPTS (line 15) | interface SocketOPTS {
function isLocalhostSocket (line 22) | function isLocalhostSocket(socket: Io.Socket) {
class Socket (line 27) | class Socket implements SocketOPTS {
method constructor (line 36) | constructor(opts: SocketOPTS) {
method init (line 51) | async init(): Promise<void> {
method sendRoomLocked (line 56) | sendRoomLocked(): void {
method saveRoom (line 60) | async saveRoom(room: Room): Promise<number> {
method destroyRoom (line 68) | async destroyRoom(): Promise<0 | 1> {
method fetchRoom (line 72) | async fetchRoom(): Promise<unknown> {
method joinRoom (line 80) | joinRoom(): Promise<unknown> {
method handleSocket (line 87) | handleSocket(): void {
method handleDisconnect (line 206) | async handleDisconnect(socket: Io.Socket): Promise<void> {
method disconnectAllUsers (line 237) | disconnectAllUsers(room: Room): void {
FILE: src/server/index.ts
function setStaticFileHeaders (line 61) | function setStaticFileHeaders(
class DeskreenSignalingServer (line 76) | class DeskreenSignalingServer {
method constructor (line 93) | constructor() {
method init (line 111) | init(): void {
method start (line 159) | async start(): Promise<http.Server> {
method listenCallback (line 165) | listenCallback() {
method callListenOnHttpServer (line 176) | async callListenOnHttpServer(): Promise<http.Server> {
method stop (line 234) | stop(): void {
FILE: src/server/onDeviceConnectedCallback.ts
function onDeviceConnectedCallback (line 7) | function onDeviceConnectedCallback(device: Device): void {
FILE: src/server/socketsIPService.ts
class SocketsIPService (line 1) | class SocketsIPService {
method constructor (line 8) | constructor() {
method setIPOfSocketID (line 13) | setIPOfSocketID(id: string, ip: string): void {
method setSocketIDOfIP (line 18) | setSocketIDOfIP(ip: string, id: string): void {
method getSocketIPByID (line 23) | getSocketIPByID(id: string): string | undefined {
method getSocketIDByIP (line 27) | getSocketIDByIP(ip: string): string | undefined {
method isIPExists (line 31) | isIPExists(ip: string): boolean {
method getInstance (line 35) | static getInstance(): SocketsIPService {
FILE: src/server/startPollForInactiveRooms.ts
function startPollForInactiveRooms (line 7) | async function startPollForInactiveRooms(): Promise<void> {
FILE: src/server/store/MemoryStore.ts
type MemoryStoreBucket (line 6) | type MemoryStoreBucket = Record<string, unknown>;
type MemoryStoreData (line 7) | type MemoryStoreData = Record<string, MemoryStoreBucket>;
type MemoryStoreParams (line 9) | interface MemoryStoreParams {
class MemoryStore (line 13) | class MemoryStore implements MemoryStoreParams {
method constructor (line 16) | constructor() {
method get (line 20) | async get(key: string, field: string): Promise<unknown | null> {
method getAll (line 28) | async getAll(key: string): Promise<MemoryStoreBucket> {
method set (line 32) | async set(key: string, field: string, value: unknown): Promise<number> {
method del (line 38) | async del(key: string, field: string): Promise<0 | 1> {
method inc (line 47) | async inc(key: string, field: string, inc = 1): Promise<number> {
method ensureBucket (line 56) | private ensureBucket(key: string): MemoryStoreBucket {
FILE: src/server/store/socketIOServerStore.ts
class SocketIOServerStore (line 3) | class SocketIOServerStore {
method setServer (line 6) | setServer(server: Io.Server): void {
method getServer (line 10) | getServer(): Io.Server {
Condensed preview — 266 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (897K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 170,
"preview": "# TODO: edit funding when ready\n\n# These are supported funding model platforms\n\n# github: [deskreen, pavlobu]\npatreon: d"
},
{
"path": ".github/ISSUE_TEMPLATE/1-Bug_report.md",
"chars": 1789,
"preview": "---\nname: Bug report\nabout: You're having technical issues. 🐞 And willing to share more details. If you don't know detai"
},
{
"path": ".github/ISSUE_TEMPLATE/3-Feature_request.md",
"chars": 767,
"preview": "---\nname: Feature request\nabout: You want something small added to Deskreen and with concrete code and solution. 🎉 If it"
},
{
"path": ".github/config.yml",
"chars": 121,
"preview": "requiredHeaders:\n - Prerequisites\n - Expected Behavior\n - Current Behavior\n - Possible Solution\n - Your Environment"
},
{
"path": ".github/stale.yml",
"chars": 640,
"preview": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 60\n# Number of days of inactivity before a "
},
{
"path": ".github/workflows/release.yml",
"chars": 7928,
"preview": "on:\n push:\n # Sequence of patterns matched against refs/tags\n tags:\n - 'v*' # Push events to matching v*, i."
},
{
"path": ".gitignore",
"chars": 197,
"preview": ".history\nnode_modules\ndist\nout\n.DS_Store\n.pnpm-store\n.eslintcache\n*.log*\nsrc/client-viewer/dist\nsrc/client-viewer/node_m"
},
{
"path": "CHANGELOG.md",
"chars": 2562,
"preview": "# 1.0.11 (8 Mar 2021)\n\nChanges:\n\n- add Danish language translations. Special thanks to @LauritsLL\n- add German language "
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3352,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "LICENSE",
"chars": 34523,
"preview": " GNU AFFERO GENERAL PUBLIC LICENSE\n Version 3, 19 November 2007\n\n Copyright (C)"
},
{
"path": "README.md",
"chars": 4074,
"preview": "# Deskreen CE (Community Edition)\n\n;\nconst path = require('node:path');\nconst { ex"
},
{
"path": "scripts/undo-version-bump.js",
"chars": 5457,
"preview": "#!/usr/bin/env node\n'use strict';\n\nconst fs = require('node:fs/promises');\nconst path = require('node:path');\nconst { ex"
},
{
"path": "src/client-viewer/.gitignore",
"chars": 253,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndis"
},
{
"path": "src/client-viewer/README.md",
"chars": 110,
"preview": "# Deskreen CE Client-Viewer\n\nAGPL-3.0 License © [Pavlo (Paul) Buidenkov](https://github.com/pavlobu/deskreen)\n"
},
{
"path": "src/client-viewer/index.html",
"chars": 6288,
"preview": "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <link rel=\"icon\" type=\"image/x-icon\" href=\"/f"
},
{
"path": "src/client-viewer/package.json",
"chars": 1296,
"preview": "{\n \"name\": \"deskreen-ce-client-viewer\",\n \"private\": true,\n \"version\": \"3.2.13\",\n \"type\": \"module\",\n \"scripts\": {\n "
},
{
"path": "src/client-viewer/public/img/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "src/client-viewer/public/locales/da/translation.json",
"chars": 6567,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Venter på at brugeren klikker TILLAD knappen p"
},
{
"path": "src/client-viewer/public/locales/de/translation.json",
"chars": 6706,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Warten bis der Nutzer auf dem Freigabegerät au"
},
{
"path": "src/client-viewer/public/locales/en/translation.json",
"chars": 6239,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Waiting for video stream of screen sharing dev"
},
{
"path": "src/client-viewer/public/locales/es/translation.json",
"chars": 7103,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Esperando que el usuario haga clic en el botón"
},
{
"path": "src/client-viewer/public/locales/fi/translation.json",
"chars": 6894,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Odotetaan että käyttäjä napsauttaa SALLI-paini"
},
{
"path": "src/client-viewer/public/locales/fr/translation.json",
"chars": 7094,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"En attente de la validation depuis l'appareil "
},
{
"path": "src/client-viewer/public/locales/it/translation.json",
"chars": 6959,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"In attesa che l'utente faccia clic sul pulsant"
},
{
"path": "src/client-viewer/public/locales/ja/translation.json",
"chars": 5134,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"画面共有デバイスでユーザーが「許可」をクリックするのを待っています...\",\n\t\"Waiti"
},
{
"path": "src/client-viewer/public/locales/ko/translation.json",
"chars": 5023,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"공유할 기기의 사용자가 화면 공유 허용 버튼을 클릭하기를 기다리는 중 ...\",\n\t"
},
{
"path": "src/client-viewer/public/locales/nl/translation.json",
"chars": 6714,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Wachtend op de gebruiker om de TOESTAAN knop i"
},
{
"path": "src/client-viewer/public/locales/ru/translation.json",
"chars": 6879,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Ждем когда пользователь нажмет кнопку РАЗРЕШИТ"
},
{
"path": "src/client-viewer/public/locales/sv/translation.json",
"chars": 6567,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Väntar på att användaren ska klicka på 'TILLÅT"
},
{
"path": "src/client-viewer/public/locales/ua/translation.json",
"chars": 6841,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Чекаємо коли користувач натисне кнопку ДОЗВОЛИ"
},
{
"path": "src/client-viewer/public/locales/zh_CN/translation.json",
"chars": 4615,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"正在等待用户单击屏幕共享设备上的允许按钮...\",\n\t\"Waiting for user t"
},
{
"path": "src/client-viewer/public/locales/zh_TW/translation.json",
"chars": 4624,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"正在等待使用者單擊螢幕共享裝置上的允許按鈕...\",\n\t\"Waiting for user "
},
{
"path": "src/client-viewer/public/manifest.json",
"chars": 486,
"preview": "{\n\t\"short_name\": \"Deskreen CE\",\n\t\"name\": \"Deskreen CE Makes Any Device a Second Screen For Your Computer\",\n\t\"icons\": [\n\t"
},
{
"path": "src/client-viewer/public/robots.txt",
"chars": 67,
"preview": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n"
},
{
"path": "src/client-viewer/scripts/ga-interceptor.js",
"chars": 3915,
"preview": "(function () {\n\tconst CONSENT_KEY = 'deskreen_ga_consent';\n\tconst GA_DOMAINS = [\n\t\t'google-analytics.com',\n\t\t'googletagm"
},
{
"path": "src/client-viewer/src/App.css",
"chars": 582,
"preview": "#root {\n\tmax-width: 1280px;\n\tmargin: 0 auto;\n\tpadding: 2rem;\n\ttext-align: center;\n}\n\n.logo {\n\theight: 6em;\n\tpadding: 1.5"
},
{
"path": "src/client-viewer/src/App.tsx",
"chars": 4511,
"preview": "import React, { useEffect, useState } from 'react';\nimport MainView from './containers/MainView';\nimport PrivacyConsentD"
},
{
"path": "src/client-viewer/src/api/config.ts",
"chars": 196,
"preview": "let host;\nlet protocol;\nlet port;\n\nif (!host && !protocol && !port) {\n\thost = window.location.host.split(':')[0];\n\tproto"
},
{
"path": "src/client-viewer/src/api/generator.ts",
"chars": 300,
"preview": "import config from './config';\n\nexport const generateUrl = (resourceName = '') => {\n\tconst { port, protocol, host } = co"
},
{
"path": "src/client-viewer/src/assets/index.html",
"chars": 1730,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <link rel=\"icon\" href=\"%PUBLIC_URL%/favicon.i"
},
{
"path": "src/client-viewer/src/assets/locales/da/translation.json",
"chars": 5600,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Venter på at brugeren klikker TILLAD knappen p"
},
{
"path": "src/client-viewer/src/assets/locales/de/translation.json",
"chars": 5655,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Warten bis der Nutzer auf dem Freigabegerät au"
},
{
"path": "src/client-viewer/src/assets/locales/en/translation.json",
"chars": 3249,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Waiting for video stream of screen sharing dev"
},
{
"path": "src/client-viewer/src/assets/locales/es/translation.json",
"chars": 6068,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Esperando que el usuario haga clic en el botón"
},
{
"path": "src/client-viewer/src/assets/locales/fi/translation.json",
"chars": 5843,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Odotetaan että käyttäjä napsauttaa SALLI-paini"
},
{
"path": "src/client-viewer/src/assets/locales/fr/translation.json",
"chars": 6037,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"En attente de la validation depuis l'appareil "
},
{
"path": "src/client-viewer/src/assets/locales/it/translation.json",
"chars": 5933,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"In attesa che l'utente faccia clic sul pulsant"
},
{
"path": "src/client-viewer/src/assets/locales/ja/translation.json",
"chars": 4400,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"画面共有デバイスでユーザーが「許可」をクリックするのを待っています...\",\n\t\"Waiti"
},
{
"path": "src/client-viewer/src/assets/locales/ko/translation.json",
"chars": 4297,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"공유할 기기의 사용자가 화면 공유 허용 버튼을 클릭하기를 기다리는 중 ...\",\n\t"
},
{
"path": "src/client-viewer/src/assets/locales/nl/translation.json",
"chars": 5714,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Wachtend op de gebruiker om de TOESTAAN knop i"
},
{
"path": "src/client-viewer/src/assets/locales/ru/translation.json",
"chars": 5840,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Ждем когда пользователь нажмет кнопку РАЗРЕШИТ"
},
{
"path": "src/client-viewer/src/assets/locales/sv/translation.json",
"chars": 5608,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Väntar på att användaren ska klicka på 'TILLÅT"
},
{
"path": "src/client-viewer/src/assets/locales/ua/translation.json",
"chars": 5821,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"Чекаємо коли користувач натисне кнопку ДОЗВОЛИ"
},
{
"path": "src/client-viewer/src/assets/locales/zh_CN/translation.json",
"chars": 3972,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"正在等待用户单击屏幕共享设备上的允许按钮...\",\n\t\"Waiting for user t"
},
{
"path": "src/client-viewer/src/assets/locales/zh_TW/translation.json",
"chars": 3983,
"preview": "{\n\t\"Waiting for user to click ALLOW button on screen sharing device...\": \"正在等待使用者單擊螢幕共享裝置上的允許按鈕...\",\n\t\"Waiting for user "
},
{
"path": "src/client-viewer/src/assets/manifest.json",
"chars": 486,
"preview": "{\n\t\"short_name\": \"Deskreen CE\",\n\t\"name\": \"Deskreen CE Makes Any Device a Second Screen For Your Computer\",\n\t\"icons\": [\n\t"
},
{
"path": "src/client-viewer/src/assets/robots.txt",
"chars": 67,
"preview": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n"
},
{
"path": "src/client-viewer/src/components/ConnectingIndicator/ConnectingIndicatorIcon.tsx",
"chars": 619,
"preview": "import { Icon } from '@blueprintjs/core';\nimport { Col, Row } from 'react-flexbox-grid';\n\ninterface ConnectingIndicatorI"
},
{
"path": "src/client-viewer/src/components/ConnectingIndicator/LoadingSharingIcon.tsx",
"chars": 1075,
"preview": "import { Icon } from '@blueprintjs/core';\nimport { Col, Row } from 'react-flexbox-grid';\nimport PropagateLoader from 're"
},
{
"path": "src/client-viewer/src/components/ConnectingIndicator/index.tsx",
"chars": 2718,
"preview": "import React from 'react';\nimport { Text } from '@blueprintjs/core';\nimport { Row } from 'react-flexbox-grid';\nimport Co"
},
{
"path": "src/client-viewer/src/components/ErrorDialog/ErrorMessageEnum.ts",
"chars": 351,
"preview": "export const ErrorMessage = {\n\tUNKNOWN_ERROR: 'An unknown error occurred',\n\tDENY_TO_CONNECT: 'You were not allowed to co"
},
{
"path": "src/client-viewer/src/components/ErrorDialog/index.css",
"chars": 56,
"preview": ".error-dialog-backdrop {\n\tbackdrop-filter: blur(2px);\n}\n"
},
{
"path": "src/client-viewer/src/components/ErrorDialog/index.tsx",
"chars": 1754,
"preview": "import { Classes, Dialog, Divider, H1, H2, H3, Icon } from '@blueprintjs/core';\nimport { Col, Row } from 'react-flexbox-"
},
{
"path": "src/client-viewer/src/components/LoadingScreen/LoadingScreen.css",
"chars": 416,
"preview": ".loading-screen {\n\tposition: fixed;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: flex;\n\talign-items: center"
},
{
"path": "src/client-viewer/src/components/LoadingScreen/index.tsx",
"chars": 225,
"preview": "import React from 'react';\nimport './LoadingScreen.css';\n\nconst LoadingScreen: React.FC = () => {\n\treturn (\n\t\t<div class"
},
{
"path": "src/client-viewer/src/components/MyDeviceInfoCard/DeviceDetails.d.ts",
"chars": 120,
"preview": "interface DeviceDetails {\n\tmyIP: string;\n\tmyOS: string;\n\tmyDeviceType: string;\n\tmyBrowser: string;\n\tmyRoomId: string;\n}\n"
},
{
"path": "src/client-viewer/src/components/MyDeviceInfoCard/index.tsx",
"chars": 1597,
"preview": "import { Callout, Card, H3, Text, Tooltip, Position } from '@blueprintjs/core';\nimport { useTranslation } from 'react-i1"
},
{
"path": "src/client-viewer/src/components/PlayerControlPanel/handlePlayerToggleFullscreen.ts",
"chars": 161,
"preview": "import { togglePlayerFullscreen } from '../../utils/playerFullscreen';\n\nexport const handlePlayerToggleFullscreen = () ="
},
{
"path": "src/client-viewer/src/components/PlayerControlPanel/index.css",
"chars": 3076,
"preview": ".play-pause-text {\n\twidth: max-content;\n}\n\n.play-pause-button,\n.play-pause-button:focus,\n.play-pause-button:active {\n\tbo"
},
{
"path": "src/client-viewer/src/components/PlayerControlPanel/index.tsx",
"chars": 13514,
"preview": "import React, { useEffect, useState, useCallback } from 'react';\nimport {\n\tAlignment,\n\tButton,\n\tButtonGroup,\n\tCard,\n\tH5,"
},
{
"path": "src/client-viewer/src/components/PlayerControlPanel/initScreenfullOnChange.ts",
"chars": 260,
"preview": "import { subscribeToPlayerFullscreenChange } from '../../utils/playerFullscreen';\n\nexport default (setIsFullScreenOn: (_"
},
{
"path": "src/client-viewer/src/components/PrivacyConsentDialog/index.css",
"chars": 1977,
"preview": ".privacy-consent-dialog-backdrop {\n\tbackdrop-filter: blur(2px);\n}\n\n.privacy-consent-dialog {\n\tdisplay: flex;\n\tflex-direc"
},
{
"path": "src/client-viewer/src/components/PrivacyConsentDialog/index.tsx",
"chars": 6799,
"preview": "import { useEffect, useState } from 'react';\nimport {\n\tButton,\n\tClasses,\n\tDialog,\n\tDivider,\n\tH2,\n\tH3,\n\tHTMLSelect,\n\tIcon"
},
{
"path": "src/client-viewer/src/components/PrivacyControlDialog/index.css",
"chars": 1621,
"preview": ".privacy-control-dialog-backdrop {\n\tbackdrop-filter: blur(2px);\n}\n\n.privacy-control-dialog {\n\tdisplay: flex;\n\tflex-direc"
},
{
"path": "src/client-viewer/src/components/PrivacyControlDialog/index.tsx",
"chars": 7754,
"preview": "import { useEffect, useState } from 'react';\nimport {\n\tButton,\n\tClasses,\n\tDialog,\n\tDivider,\n\tH2,\n\tH3,\n\tHTMLSelect,\n\tIcon"
},
{
"path": "src/client-viewer/src/components/VideoJSPlayer/index.tsx",
"chars": 2998,
"preview": "import { useEffect, useRef } from 'react';\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore n"
},
{
"path": "src/client-viewer/src/components/VideoJSPlayer/videojs-contain.css",
"chars": 462,
"preview": ".video-js,\n.video-js .vjs-tech {\n\twidth: 100% !important;\n\theight: 100% !important;\n\tbackground-color: #000 !important;\n"
},
{
"path": "src/client-viewer/src/config/i18n.ts",
"chars": 1850,
"preview": "/* istanbul ignore file */\n\nimport i18n from 'i18next';\nimport { initReactI18next } from 'react-i18next';\nimport Backend"
},
{
"path": "src/client-viewer/src/constants/appConstants.ts",
"chars": 418,
"preview": "export const PLAYER_WRAPPER_ID = 'player-wrapper-id';\nexport const VIDEO_QUALITY_TO_DECIMAL = {\n\t'25%': 0.25, // Q_25_PE"
},
{
"path": "src/client-viewer/src/constants/styleConstants.ts",
"chars": 61,
"preview": "export const LIGHT_UI_BACKGROUND = 'rgba(240, 248, 250, 1)';\n"
},
{
"path": "src/client-viewer/src/containers/ConnectionPrompts/index.tsx",
"chars": 3383,
"preview": "import { Row, Col } from 'react-flexbox-grid';\nimport { useTranslation } from 'react-i18next';\nimport { LIGHT_UI_BACKGRO"
},
{
"path": "src/client-viewer/src/containers/MainView/ConnectionIconEnum.ts",
"chars": 121,
"preview": "const ConnectionIcon = {\n\tFEED: 'feed',\n\tFEED_SUBSCRIBED: 'feed-subscribed',\n} as const;\n\nexport default ConnectionIcon;"
},
{
"path": "src/client-viewer/src/containers/MainView/LoadingSharingIconEnum.ts",
"chars": 211,
"preview": "export const LoadingSharingIconEnum = {\n\tDESKTOP: 'desktop',\n\tAPPLICATION: 'application',\n} as const;\n\nexport type Loadi"
},
{
"path": "src/client-viewer/src/containers/MainView/changeLanguage.ts",
"chars": 103,
"preview": "import i18n from '../../config/i18n';\n\nexport default (lng: string) => {\n\ti18n.changeLanguage(lng);\n};\n"
},
{
"path": "src/client-viewer/src/containers/MainView/handleCreatePeerConnection.ts",
"chars": 1646,
"preview": "import PeerConnection from '../../features/PeerConnection';\nimport PeerConnectionUIHandler from '../../features/PeerConn"
},
{
"path": "src/client-viewer/src/containers/MainView/handleDisplayingLoadingSharingIconLoop.ts",
"chars": 1092,
"preview": "import { LoadingSharingIconEnum } from './LoadingSharingIconEnum';\n\nexport default (params: handleDisplayingLoadingShari"
},
{
"path": "src/client-viewer/src/containers/MainView/handleNoConnectionTimeout.ts",
"chars": 387,
"preview": "import { DUMMY_MY_DEVICE_DETAILS } from '../../constants/appConstants';\n\nexport default (\n\tmyDeviceDetails: DeviceDetail"
},
{
"path": "src/client-viewer/src/containers/MainView/handleRemoveDanglingReactRevealContainer.ts",
"chars": 237,
"preview": "export default (url: MediaStream | null) => {\n\treturn () => {\n\t\tif (url !== null) {\n\t\t\tsetTimeout(() => {\n\t\t\t\t// @ts-ign"
},
{
"path": "src/client-viewer/src/containers/MainView/handleSetVideoQuality.ts",
"chars": 371,
"preview": "import PeerConnection from '../../features/PeerConnection';\nimport { type VideoQualityType } from '../../features/VideoA"
},
{
"path": "src/client-viewer/src/containers/MainView/index.css",
"chars": 1102,
"preview": "@media (prefers-reduced-motion: no-preference) {\n\t.App-logo {\n\t\tanimation: App-logo-spin infinite 20s linear;\n\t}\n\n\t#puls"
},
{
"path": "src/client-viewer/src/containers/MainView/index.tsx",
"chars": 4881,
"preview": "import { useEffect, useState, useCallback } from 'react';\nimport { Grid } from 'react-flexbox-grid';\nimport screenfull f"
},
{
"path": "src/client-viewer/src/containers/MainView/useScreenViewingTracker.ts",
"chars": 4818,
"preview": "import { useEffect, useRef } from 'react';\nimport { trackAnalyticsEvent } from '../../utils/analytics';\nimport { type Er"
},
{
"path": "src/client-viewer/src/containers/PlayerView/index.tsx",
"chars": 7516,
"preview": "import { useEffect, useRef, useCallback } from 'react';\nimport { OverlayToaster, Position } from '@blueprintjs/core';\nim"
},
{
"path": "src/client-viewer/src/features/PeerConnection/NullUser.ts",
"chars": 41,
"preview": "export default { username: '', id: '' };\n"
},
{
"path": "src/client-viewer/src/features/PeerConnection/PartnerPeerUser.d.ts",
"chars": 49,
"preview": "interface PartnerPeerUser {\n\tusername: string;\n}\n"
},
{
"path": "src/client-viewer/src/features/PeerConnection/PeerConnection.d.ts",
"chars": 43,
"preview": "type PeerConnection = import('.').default;\n"
},
{
"path": "src/client-viewer/src/features/PeerConnection/PeerConnectionUIHandler.ts",
"chars": 1316,
"preview": "import {\n\tErrorMessage,\n\ttype ErrorMessageType,\n} from '../../components/ErrorDialog/ErrorMessageEnum';\n\nexport default "
},
{
"path": "src/client-viewer/src/features/PeerConnection/ReceiveEncryptedMessagePayload.d.ts",
"chars": 118,
"preview": "interface ReceiveEncryptedMessagePayload {\n\tfromSocketID: string;\n\ttype: string;\n\tpayload: Record<string, unknown>;\n}\n"
},
{
"path": "src/client-viewer/src/features/PeerConnection/ScreenSharingSourceEnum.ts",
"chars": 191,
"preview": "export const ScreenSharingSource = {\n\tWINDOW: 'window',\n\tSCREEN: 'screen',\n} as const;\n\nexport type ScreenSharingSourceT"
},
{
"path": "src/client-viewer/src/features/PeerConnection/errors/PeerConnectionPartnerIsNotDefinedError.ts",
"chars": 264,
"preview": "export default class PeerConnectionPartnerIsNotDefinedError extends Error {\n\tconstructor() {\n\t\tsuper('partner should be "
},
{
"path": "src/client-viewer/src/features/PeerConnection/errors/PeerConnectionPeerIsNullError.ts",
"chars": 251,
"preview": "export default class PeerConnectionPeerIsNullError extends Error {\n\tconstructor() {\n\t\tsuper('peer of PeerConnection shou"
},
{
"path": "src/client-viewer/src/features/PeerConnection/errors/PeerConnectionSocketNotDefined.ts",
"chars": 236,
"preview": "export default class PeerConnectionSocketNotDefined extends Error {\n\tconstructor() {\n\t\tsuper('socket should be defined!'"
},
{
"path": "src/client-viewer/src/features/PeerConnection/errors/PeerConnectionUserIsNotDefinedError.ts",
"chars": 244,
"preview": "export default class PeerConnectionUserIsNotDefinedError extends Error {\n\tconstructor() {\n\t\tsuper('user should be define"
},
{
"path": "src/client-viewer/src/features/PeerConnection/index.ts",
"chars": 7778,
"preview": "import shortId from 'shortid';\nimport SimplePeer from 'simple-peer';\nimport { UAParser } from 'ua-parser-js';\nimport typ"
},
{
"path": "src/client-viewer/src/features/PeerConnection/mocks/INPUTtestWindowNavigatorUserAgent.ts",
"chars": 174,
"preview": "export const INPUTtestWindowNavigatorUserAgent =\n\t'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (K"
},
{
"path": "src/client-viewer/src/features/PeerConnection/mocks/INPUTvideo500000testSdpMediaBitrate.ts",
"chars": 3262,
"preview": "export const INPUTtestSdpMediaBitrate = `\nv=0\no=- 5730467698688819135 2 IN IP4 127.0.0.1\ns=-\nt=0 0\na=group:BUNDLE 0 1\na="
},
{
"path": "src/client-viewer/src/features/PeerConnection/mocks/OUTPUTDeviceDetailsFromUAParsed.ts",
"chars": 203,
"preview": "export const OUTPUTDeviceDetailsFromUAParsed: DeviceDetails = {\n\tmyBrowser: 'Chrome 87.0.4280.88',\n\tmyDeviceType: 'compu"
},
{
"path": "src/client-viewer/src/features/PeerConnection/mocks/OUTPUTvideo500000testSdpMediaBitrate.ts",
"chars": 3275,
"preview": "export const OUTPUTtestSdpMediaBitrate = `\nv=0\no=- 5730467698688819135 2 IN IP4 127.0.0.1\ns=-\nt=0 0\na=group:BUNDLE 0 1\na"
},
{
"path": "src/client-viewer/src/features/PeerConnection/peerConnectionHandlePeer.ts",
"chars": 2617,
"preview": "import {\n\tprepareDataMessageToChangeQuality,\n\tprepareDataMessageToGetSharingSourceType,\n} from './simplePeerDataMessages"
},
{
"path": "src/client-viewer/src/features/PeerConnection/peerConnectionHandleSocket.ts",
"chars": 4561,
"preview": "import { ErrorMessage } from '../../components/ErrorDialog/ErrorMessageEnum';\nimport {\n\tgetBrowserFromUAParser,\n\tgetDevi"
},
{
"path": "src/client-viewer/src/features/PeerConnection/peerConnectionReceiveEncryptedMessage.ts",
"chars": 1222,
"preview": "import { ErrorMessage } from '../../components/ErrorDialog/ErrorMessageEnum';\nimport { process as processMessage } from "
},
{
"path": "src/client-viewer/src/features/PeerConnection/setAndShowErrorDialogMessage.ts",
"chars": 803,
"preview": "import {\n\tErrorMessage,\n\ttype ErrorMessageType,\n} from '../../components/ErrorDialog/ErrorMessageEnum';\n\nexport default "
},
{
"path": "src/client-viewer/src/features/PeerConnection/setSdpMediaBitrate.ts",
"chars": 1079,
"preview": "export default (sdp: string, mediaType: string, bitrate: number) => {\n\tconst sdpLines = sdp.split('\\n');\n\tlet mediaLineI"
},
{
"path": "src/client-viewer/src/features/PeerConnection/simplePeerDataMessages.ts",
"chars": 335,
"preview": "export function prepareDataMessageToChangeQuality(q: number) {\n\treturn `\n {\n \"type\": \"set_video_quality\",\n "
},
{
"path": "src/client-viewer/src/features/PeerConnection/startSocketConnectedCheckingLoop/index.ts",
"chars": 1824,
"preview": "import PeerConnection from '..';\nimport { ErrorMessage } from '../../../components/ErrorDialog/ErrorMessageEnum';\nimport"
},
{
"path": "src/client-viewer/src/features/VideoAutoQualityOptimizer/VideoQualityEnum.ts",
"chars": 253,
"preview": "export const VideoQuality = {\n\tQ_AUTO: 'Auto',\n\tQ_25_PERCENT: '25%',\n\tQ_40_PERCENT: '40%',\n\tQ_60_PERCENT: '60%',\n\tQ_80_P"
},
{
"path": "src/client-viewer/src/features/VideoAutoQualityOptimizer/errors/CanvasNotDefinedError.ts",
"chars": 251,
"preview": "export default class CanvasNotDefinedError extends Error {\n\tconstructor() {\n\t\tsuper('internal variable of canvas DOM ele"
},
{
"path": "src/client-viewer/src/features/VideoAutoQualityOptimizer/errors/ImageDataIsUndefinedError.ts",
"chars": 234,
"preview": "export default class ImageDataIsUndefinedError extends Error {\n\tconstructor() {\n\t\tsuper('imageData retrieved is undefine"
},
{
"path": "src/client-viewer/src/features/VideoAutoQualityOptimizer/errors/VideoDimensionsAreWrongError.ts",
"chars": 272,
"preview": "export default class VideoDimensionsAreWrongError extends Error {\n\tconstructor() {\n\t\tsuper('video dimensions are wrong, "
},
{
"path": "src/client-viewer/src/features/VideoAutoQualityOptimizer/errors/VideoNotDefinedError.ts",
"chars": 248,
"preview": "export default class VideoNotDefinedError extends Error {\n\tconstructor() {\n\t\tsuper('internal variable of video DOM eleme"
},
{
"path": "src/client-viewer/src/features/VideoAutoQualityOptimizer/index.ts",
"chars": 5156,
"preview": "import { COMPARISON_CANVAS_ID } from './../../constants/appConstants';\nimport pixelmatch from 'pixelmatch';\nimport { PLA"
},
{
"path": "src/client-viewer/src/index.css",
"chars": 1943,
"preview": "@import \"normalize.css\";\n@import \"@blueprintjs/core/lib/css/blueprint.css\";\n/*@import \"@blueprintjs/icons/lib/css/bluepr"
},
{
"path": "src/client-viewer/src/main.tsx",
"chars": 694,
"preview": "import { initializeGARequestInterceptor } from './utils/gaRequestInterceptor';\n\n// initialize GA request interceptor imm"
},
{
"path": "src/client-viewer/src/providers/AppContextProvider/index.tsx",
"chars": 788,
"preview": "import React, { useState } from 'react';\n\ninterface AppContextInterface {\n\tappLanguage: string;\n\tsetAppLanguageHook: (va"
},
{
"path": "src/client-viewer/src/utils/ProcessedMessage.d.ts",
"chars": 1032,
"preview": "type CallUserMessageWithPayload = {\n\ttype: 'CALL_USER';\n\tpayload: {\n\t\tsignalData: string;\n\t};\n};\n\ntype DeviceDetailsMess"
},
{
"path": "src/client-viewer/src/utils/analytics.ts",
"chars": 7155,
"preview": "// google analytics type declarations\ndeclare global {\n\tinterface Window {\n\t\tdataLayer: unknown[];\n\t\tgtag: (...args: unk"
},
{
"path": "src/client-viewer/src/utils/gaRequestInterceptor.ts",
"chars": 5508,
"preview": "import { getConsentStatus } from './analytics';\n\nconst GA_DOMAINS = [\n\t'google-analytics.com',\n\t'googletagmanager.com',\n"
},
{
"path": "src/client-viewer/src/utils/message.ts",
"chars": 1118,
"preview": "import type { LocalPeerUser } from '../../../common/LocalPeerUser';\nimport type { SendEncryptedMessagePayload } from '.."
},
{
"path": "src/client-viewer/src/utils/playerFullscreen.ts",
"chars": 5729,
"preview": "import screenfull from 'screenfull';\nimport { PLAYER_WRAPPER_ID } from '../constants/appConstants';\n\ntype LegacyFullscre"
},
{
"path": "src/client-viewer/src/utils/socket.ts",
"chars": 313,
"preview": "import socketIO, { Socket } from 'socket.io-client';\nimport { generateUrl } from '../api/generator';\n\nlet socket: Socket"
},
{
"path": "src/client-viewer/src/utils/userAgentParserHelpers.ts",
"chars": 776,
"preview": "// @ts-ignore\nimport { UAParser } from 'ua-parser-js';\n\nexport function getOSFromUAParser(uaParser: UAParser) {\n\tconst o"
},
{
"path": "src/client-viewer/src/vite-env.d.ts",
"chars": 1791,
"preview": "/// <reference types=\"vite/client\" />\ntype ConnectionIconType =\n\t| ConnectionIconEnum.FEED\n\t| ConnectionIconEnum.FEED_SU"
},
{
"path": "src/client-viewer/tsconfig.app.json",
"chars": 659,
"preview": "{\n\t\"compilerOptions\": {\n\t\t\"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.app.tsbuildinfo\",\n\t\t\"target\": \"ES2022\",\n\t\t\"us"
},
{
"path": "src/client-viewer/tsconfig.json",
"chars": 112,
"preview": "{\n\t\"files\": [],\n\t\"references\": [\n\t\t{ \"path\": \"./tsconfig.app.json\" },\n\t\t{ \"path\": \"./tsconfig.node.json\" }\n\t]\n}\n"
},
{
"path": "src/client-viewer/tsconfig.node.json",
"chars": 591,
"preview": "{\n\t\"compilerOptions\": {\n\t\t\"tsBuildInfoFile\": \"./node_modules/.tmp/tsconfig.node.tsbuildinfo\",\n\t\t\"target\": \"ES2023\",\n\t\t\"l"
},
{
"path": "src/client-viewer/vite.config.ts",
"chars": 1931,
"preview": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\nimport legacy from '@vitejs/plugin-legacy"
},
{
"path": "src/common/DesktopCapturerSourceType.ts",
"chars": 118,
"preview": "enum DesktopCapturerSourceType {\n\tWINDOW = 'window',\n\tSCREEN = 'screen',\n}\n\nexport default DesktopCapturerSourceType;\n"
},
{
"path": "src/common/Device.ts",
"chars": 231,
"preview": "export interface Device {\n\tid: string;\n\tsharingSessionID: string;\n\tdeviceOS: string;\n\tdeviceType: string;\n\tdeviceIP: str"
},
{
"path": "src/common/ElectronStoreKeys.enum.ts",
"chars": 116,
"preview": "export enum ElectronStoreKeys {\n\tAppLanguage = 'appLanguage',\n\tIsNotFirstTimeAppStart = 'isNotFirstTimeAppStart',\n}\n"
},
{
"path": "src/common/IpcEvents.enum.ts",
"chars": 2227,
"preview": "export enum IpcEvents {\n\tCreateWaitingForConnectionSharingSession = 'create-waiting-for-connection-sharing-session',\n\tSe"
},
{
"path": "src/common/LocalPeerUser.ts",
"chars": 67,
"preview": "export interface LocalPeerUser {\n\tusername: string;\n\tid: string;\n}\n"
},
{
"path": "src/common/SendEncryptedMessagePayload.ts",
"chars": 99,
"preview": "export interface SendEncryptedMessagePayload {\n\ttype: string;\n\tpayload: Record<string, unknown>;\n}\n"
},
{
"path": "src/common/app.lang.config.ts",
"chars": 498,
"preview": "export default {\n\tfallbackLng: 'en',\n\tnamespace: 'translation',\n\tlanguages: [\n\t\t'ua',\n\t\t'en',\n\t\t'es',\n\t\t'zh_CN',\n\t\t'zh_T"
},
{
"path": "src/common/config.ts",
"chars": 309,
"preview": "/* istanbul ignore file */\n\nlet hostname;\nlet protocol;\nlet primaryPort;\nlet backupPort;\n\nif (!hostname && !protocol && "
},
{
"path": "src/common/connectSocket.ts",
"chars": 227,
"preview": "import socketIO from 'socket.io-client';\n\nexport const connectSocket = (port: string, roomId: string) => {\n\treturn socke"
},
{
"path": "src/common/deskreen-electron-store.ts",
"chars": 3123,
"preview": "// import Store from 'electron-store';\n//\n// export const store = new Store() as unknown as {\n// has: (key: string) =>"
},
{
"path": "src/common/getAppLanguage.ts",
"chars": 309,
"preview": "import { IpcEvents } from './IpcEvents.enum';\n\nexport default async function getAppLanguage(): Promise<string> {\n\t// esl"
},
{
"path": "src/common/isProduction.ts",
"chars": 281,
"preview": "export default function isProduction(): boolean {\n\treturn (\n\t\tprocess.env.NODE_ENV === 'production' &&\n\t\tprocess.env.RUN"
},
{
"path": "src/common/locales/da/translation.json",
"chars": 9188,
"preview": "{\n\t\"hello\": \"Hej\",\n\t\"continue\": \"Fortsæt\",\n\t\"language\": \"Sprog\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Español\",\n\t"
},
{
"path": "src/common/locales/de/translation.json",
"chars": 9565,
"preview": "{\n\t\"hello\": \"Hallo\",\n\t\"continue\": \"Weiter\",\n\t\"language\": \"Sprache\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Español\""
},
{
"path": "src/common/locales/en/translation.json",
"chars": 9887,
"preview": "{\n\t\"hello\": \"Hello\",\n\t\"continue\": \"Continue\",\n\t\"language\": \"Language\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Españ"
},
{
"path": "src/common/locales/es/translation.json",
"chars": 10079,
"preview": "{\n\t\"hello\": \"Hola\",\n\t\"continue\": \"Continuar\",\n\t\"language\": \"Idioma\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Español"
},
{
"path": "src/common/locales/fi/translation.json",
"chars": 9516,
"preview": "{\n\t\"hello\": \"Hei\",\n\t\"continue\": \"Jatkaa\",\n\t\"language\": \"Kieli\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Español\",\n\t\""
},
{
"path": "src/common/locales/fr/translation.json",
"chars": 9964,
"preview": "{\n\t\"hello\": \"Bonjour\",\n\t\"continue\": \"Continuer\",\n\t\"language\": \"Langage\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Esp"
},
{
"path": "src/common/locales/it/translation.json",
"chars": 9707,
"preview": "{\n\t\"hello\": \"Ciao\",\n\t\"continue\": \"Continua\",\n\t\"language\": \"Lingua\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Español\""
},
{
"path": "src/common/locales/ja/translation.json",
"chars": 7415,
"preview": "{\n\t\"hello\": \"こんにちは\",\n\t\"continue\": \"続ける\",\n\t\"language\": \"言語\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Español\",\n\t\"ua\":"
},
{
"path": "src/common/locales/ko/translation.json",
"chars": 7245,
"preview": "{\n\t\"hello\": \"안녕하세요\",\n\t\"continue\": \"계속\",\n\t\"language\": \"언어\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Español\",\n\t\"ua\": "
},
{
"path": "src/common/locales/nl/translation.json",
"chars": 9634,
"preview": "{\n\t\"hello\": \"Hallo\",\n\t\"continue\": \"Verder\",\n\t\"language\": \"Taal\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Español\",\n\t"
},
{
"path": "src/common/locales/ru/translation.json",
"chars": 9415,
"preview": "{\n\t\"hello\": \"Привет\",\n\t\"continue\": \"Продолжить\",\n\t\"language\": \"Язык\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Españo"
},
{
"path": "src/common/locales/sv/translation.json",
"chars": 9433,
"preview": "{\n\t\"hello\": \"Hej\",\n\t\"continue\": \"Fortsätt\",\n\t\"language\": \"Språk\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Español\",\n"
},
{
"path": "src/common/locales/ua/translation.json",
"chars": 9424,
"preview": "{\n\t\"hello\": \"Привіт\",\n\t\"continue\": \"Продовжити\",\n\t\"language\": \"Мова\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Españo"
},
{
"path": "src/common/locales/zh_CN/translation.json",
"chars": 6631,
"preview": "{\n\t\"hello\": \"您好\",\n\t\"continue\": \"继续\",\n\t\"language\": \"语言\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Español\",\n\t\"ua\": \"Ук"
},
{
"path": "src/common/locales/zh_TW/translation.json",
"chars": 6634,
"preview": "{\n\t\"hello\": \"您好\",\n\t\"continue\": \"繼續\",\n\t\"language\": \"語言\",\n\t\"ru\": \"Русский\",\n\t\"en\": \"English\",\n\t\"es\": \"Español\",\n\t\"ua\": \"Ук"
},
{
"path": "src/common/rateLimitedConsole.ts",
"chars": 3895,
"preview": "// rate-limited console wrapper to prevent log spam and memory bloat\n// this helps prevent excessive console.log calls f"
},
{
"path": "src/features/ConnectedDevicesService/index.ts",
"chars": 2976,
"preview": "import { Device } from '../../common/Device';\n\nexport const nullDevice: Device = {\n\tid: '',\n\tsharingSessionID: '',\n\tdevi"
},
{
"path": "src/features/DesktopCapturerSourcesService/index.ts",
"chars": 7727,
"preview": "/* eslint-disable no-async-promise-executor */\n/* eslint-disable @typescript-eslint/no-unused-vars */\n\nimport { desktopC"
},
{
"path": "src/features/PeerConnectionHelperRendererService/index.ts",
"chars": 1726,
"preview": "import { join } from 'path';\nimport { BrowserWindow } from 'electron';\nimport { is } from '@electron-toolkit/utils';\n\nty"
},
{
"path": "src/features/SharingSessionService/SharingSession.ts",
"chars": 4429,
"preview": "import { BrowserWindow } from 'electron';\nimport uuid from 'uuid';\nimport SharingSessionStatusEnum from './SharingSessio"
},
{
"path": "src/features/SharingSessionService/SharingSessionStatusEnum.ts",
"chars": 134,
"preview": "enum SharingSessionStatusEnum {\n\tNOT_CONNECTED,\n\tCONNECTED,\n\tSHARING,\n\tERROR,\n\tDESTROYED,\n}\n\nexport default SharingSessi"
},
{
"path": "src/features/SharingSessionService/SharingTypeEnum.ts",
"chars": 83,
"preview": "enum SharingTypeEnum {\n\tNOT_SET,\n\tSCREEN,\n\tAPP,\n}\n\nexport default SharingTypeEnum;\n"
},
{
"path": "src/features/SharingSessionService/index.ts",
"chars": 4346,
"preview": "import uuid from 'uuid';\nimport RoomIDService from '../../server/RoomIDService';\nimport { ConnectedDevicesService } from"
},
{
"path": "src/main/configs/i18next.config.ts",
"chars": 2549,
"preview": "import i18n from 'i18next';\nimport config from '../../common/app.lang.config';\n// import { ElectronStoreKeys } from '../"
},
{
"path": "src/main/helpers/getDeskreenGlobal.ts",
"chars": 154,
"preview": "import { DeskreenGlobal } from './initGlobals';\n\nexport const getDeskreenGlobal = (): DeskreenGlobal => {\n\treturn global"
},
{
"path": "src/main/helpers/getMyLocalIpV4.ts",
"chars": 7721,
"preview": "import os from \"os\";\n\n// Wi-Fi interface patterns for detection and prioritization\nconst macosWifiInterfaces = [\"en0\", \""
},
{
"path": "src/main/helpers/initGlobals.ts",
"chars": 1707,
"preview": "import { app } from 'electron';\nimport { ConnectedDevicesService } from '../../features/ConnectedDevicesService';\nimport"
},
{
"path": "src/main/helpers/ipcMainHandlers.ts",
"chars": 14848,
"preview": "import {\n\tDisplay,\n\tipcMain,\n\tBrowserWindow,\n\tscreen,\n\tclipboard,\n\tshell,\n} from 'electron';\nimport i18n from '../config"
},
{
"path": "src/main/helpers/isWifiConnected.ts",
"chars": 814,
"preview": "import os from 'os';\nimport { interfacesToCheck, interfacesStartsWithCheck } from './getMyLocalIpV4';\n\nexport default fu"
},
{
"path": "src/main/index.ts",
"chars": 9974,
"preview": "// override console early to catch all logs\nimport {\n\toverrideGlobalConsole,\n\tstartConsoleRateLimiting,\n} from '../commo"
},
{
"path": "src/main/menu.ts",
"chars": 5259,
"preview": "import {\n\tMenu,\n\tshell,\n\tBrowserWindow,\n\tMenuItemConstructorOptions,\n\tapp,\n} from 'electron';\n\nimport i18nType from './c"
},
{
"path": "src/main/utils/LoggerWithFilePrefix.ts",
"chars": 2401,
"preview": "import log from 'electron-log';\n\nlog.transports.file.level = 'warn';\n\nif (process.env.NODE_ENV !== 'production') {\n\tlog."
},
{
"path": "src/main/utils/getNewVersionTag.ts",
"chars": 640,
"preview": "import axios from 'axios';\n\nconst githubApiRepoLatestReleaseUrl =\n\t'https://api.github.com/repos/pavlobu/deskreen/releas"
},
{
"path": "src/main/utils/installExtensions.ts",
"chars": 598,
"preview": "import {\n\tinstallExtension,\n\tREACT_DEVELOPER_TOOLS,\n} from 'electron-devtools-installer';\n\nexport default async function"
},
{
"path": "src/main/utils/isLinuxWaylandSession.ts",
"chars": 205,
"preview": "const isLinuxWaylandSession =\n\tprocess.platform === 'linux' &&\n\t(process.env.XDG_SESSION_TYPE?.toLowerCase() === 'waylan"
},
{
"path": "src/preload/index.d.ts",
"chars": 221,
"preview": "import { ElectronAPI } from '@electron-toolkit/preload';\nimport forge from 'node-forge';\n\ndeclare global {\n\tinterface Wi"
},
{
"path": "src/preload/index.ts",
"chars": 1102,
"preview": "import { contextBridge } from 'electron';\nimport { electronAPI } from '@electron-toolkit/preload';\nimport forge from 'no"
},
{
"path": "src/renderer/index.html",
"chars": 486,
"preview": "<!doctype html>\n<html>\n <head>\n <meta charset=\"UTF-8\" />\n\t <title>Deskreen CE</title>\n <!-- https://developer.mo"
},
{
"path": "src/renderer/peerConnectionHelperRendererWindowIndex.html",
"chars": 679,
"preview": "<!doctype html>\n<html>\n <head>\n <meta charset=\"UTF-8\" />\n <title>Electron Helper Renderer</title>\n <!-- https:"
},
{
"path": "src/renderer/src/assets/base.css",
"chars": 11667,
"preview": "/*:root {*/\n/* --ev-c-white: #ffffff;*/\n/* --ev-c-white-soft: #f8f8f8;*/\n/* --ev-c-white-mute: #f2f2f2;*/\n\n/* --ev-c"
},
{
"path": "src/renderer/src/assets/main.css",
"chars": 3627,
"preview": "@import \"./base.css\";\n\n/*body {*/\n/* display: flex;*/\n/* align-items: center;*/\n/* justify-content: center;*/\n/* ove"
},
{
"path": "src/renderer/src/components/AllowConnectionForDeviceAlert.tsx",
"chars": 1352,
"preview": "import React from 'react';\nimport { Intent, Alert, H4 } from '@blueprintjs/core';\nimport DeviceInfoCallout from './Devic"
},
{
"path": "src/renderer/src/components/CloseOverlayButton.tsx",
"chars": 964,
"preview": "import React from 'react';\nimport { createStyles, makeStyles } from '@material-ui/core/styles';\nimport { Button, Icon } "
},
{
"path": "src/renderer/src/components/ConnectedDevicesListDrawer.tsx",
"chars": 7981,
"preview": "import { useEffect, useState, useCallback } from 'react';\nimport {\n\tButton,\n\tText,\n\tPosition,\n\tDrawer,\n\tCard,\n\tAlert,\n\tH"
},
{
"path": "src/renderer/src/components/DeviceInfoCallout/index.tsx",
"chars": 2061,
"preview": "import React from 'react';\nimport { Callout, Text, H4, Tooltip, Position } from '@blueprintjs/core';\nimport { Row, Col }"
}
]
// ... and 66 more files (download for full content)
About this extraction
This page contains the full source code of the pavlobu/deskreen GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 266 files (774.9 KB), approximately 229.6k tokens, and a symbol index with 467 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.