Compare commits

..

1 Commits

Author SHA1 Message Date
R Midhun Suresh
a85b7a2d58 Revert "Enable plain text encryption early if we actually mean to use basic_text as backend" 2025-05-23 12:24:47 +05:30
74 changed files with 3513 additions and 4523 deletions

View File

@@ -107,7 +107,7 @@ jobs:
environment: ${{ needs.prepare.outputs.deploy == 'true' && 'packages.element.io' || '' }}
steps:
- name: Download artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
- name: Prepare artifacts for deployment
run: |
@@ -197,7 +197,7 @@ jobs:
- name: Stash packages.element.io
if: needs.prepare.outputs.deploy == 'false'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: packages.element.io
path: packages.element.io
@@ -216,7 +216,7 @@ jobs:
- name: Notify packages.element.io of new files
if: needs.prepare.outputs.deploy == 'true'
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4
uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3
with:
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
repository: element-hq/packages.element.io
@@ -235,7 +235,7 @@ jobs:
- name: Stash debs
if: needs.prepare.outputs.deploy == 'false' && needs.linux.result == 'success'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: debs
path: |
@@ -274,14 +274,14 @@ jobs:
id-token: write # This is required for requesting the JWT
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4
with:
role-to-assume: arn:aws:iam::264135176173:role/Push-ElementDesktop-MSI
role-session-name: githubaction-run-${{ github.run_id }}
aws-region: ${{ env.AWS_REGION }}
- name: Download artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: win-*

View File

@@ -51,11 +51,11 @@ jobs:
tests-done:
needs: [windows, linux, macos]
runs-on: ubuntu-24.04
if: ${{ !cancelled() }}
if: always()
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
cache: "yarn"
node-version: "lts/*"
@@ -64,7 +64,7 @@ jobs:
run: yarn install --frozen-lockfile
- name: Download blob reports from GitHub Actions Artifacts
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: blob-report-*
path: all-blob-reports
@@ -75,7 +75,7 @@ jobs:
- name: Upload HTML report
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: html-report
path: playwright-report

View File

@@ -4,10 +4,6 @@
on:
workflow_call:
inputs:
ref:
type: string
required: false
description: "The git ref to checkout, defaults to the default branch"
arch:
type: string
required: true
@@ -24,62 +20,21 @@ on:
type: boolean
required: false
description: "Whether to run the blob report"
prepare-artifact-name:
type: string
required: false
description: |
The name of the prepare artifact to use, defaults to 'webapp'.
The artifact must contain the following:
+ webapp.asar - the asar archive of the webapp to embed in the desktop app
+ electronVersion - the version of electron to use for cache keying
+ hakHash - the hash of the .hak directory to use for cache keying
+ changelog.Debian - the changelog file to embed in the Debian package
+ variant.json - the variant configuration to use for the build
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
for example icons in the `build/` directory to override the app icons.
default: "webapp"
test:
type: boolean
required: false
default: true
description: "Whether to run the test stage after building"
test-args:
type: string
required: false
description: "Additional arguments to pass to playwright"
runs-on:
type: string
required: false
description: "The runner image to use, normally set for you, may be needed for running in private repos."
artifact-prefix:
type: string
required: false
description: "An optional prefix to add to the artifact name, useful for distinguishing builds in private repos."
default: ""
targets:
type: string
required: false
description: "List of targets to build"
default: "tar.gz deb"
env:
SQLCIPHER_BUNDLED: ${{ inputs.sqlcipher == 'static' && '1' || '' }}
MAX_GLIBC: 2.31 # bullseye-era glibc, used by glibc-check.sh
permissions: {} # No permissions required
jobs:
build:
name: Build Linux ${{ inputs.arch }} SQLCipher ${{ inputs.sqlcipher }}
# We build on native infrastructure as matrix-seshat fails to cross-compile properly
# https://github.com/matrix-org/seshat/issues/135
runs-on: ${{ inputs.runs-on || (inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04') }}
runs-on: ${{ inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04' }}
env:
HAK_DOCKER_IMAGE: ghcr.io/element-hq/element-desktop-dockerbuild
steps:
- name: Resolve docker image tag for push
if: github.event_name == 'push'
run: echo "HAK_DOCKER_IMAGE=$HAK_DOCKER_IMAGE:$REF" >> $GITHUB_ENV
env:
REF: ${{ inputs.ref || github.ref_name }}
run: echo "HAK_DOCKER_IMAGE=$HAK_DOCKER_IMAGE:$GITHUB_REF_NAME" >> $GITHUB_ENV
- name: Resolve docker image tag for release
if: github.event_name == 'release'
run: echo "HAK_DOCKER_IMAGE=$HAK_DOCKER_IMAGE:staging" >> $GITHUB_ENV
@@ -105,24 +60,21 @@ jobs:
}
}
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
with:
repository: element-hq/element-desktop
ref: ${{ inputs.ref }}
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: ${{ inputs.prepare-artifact-name }}
name: webapp
- name: Cache .hak
id: cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
with:
key: ${{ runner.os }}-${{ github.ref_name }}-${{ inputs.sqlcipher }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion', 'dockerbuild/*') }}
path: |
./.hak
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: .node-version
cache: "yarn"
@@ -135,7 +87,7 @@ jobs:
- name: "Get modified files"
id: changed_files
if: steps.cache.outputs.cache-hit != 'true' && github.event_name == 'pull_request' && github.repository == 'element-hq/element-desktop'
if: steps.cache.outputs.cache-hit != 'true' && github.event_name == 'pull_request'
uses: tj-actions/changed-files@823fcebdb31bb35fdf2229d9f769b400309430d0 # v46
with:
files: |
@@ -143,7 +95,7 @@ jobs:
# This allows contributors to test changes to the dockerbuild image within a pull request
- name: Build docker image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
if: steps.changed_files.outputs.any_modified == 'true'
with:
file: dockerbuild/Dockerfile
@@ -177,14 +129,19 @@ jobs:
echo "ED_DEBIAN_CHANGELOG=changelog.Debian" >> $GITHUB_ENV
fi
# Workaround for https://github.com/electron-userland/electron-builder/issues/6116
- name: Install fpm
if: inputs.arch == 'arm64'
run: |
sudo apt-get install ruby-dev build-essential
sudo gem install fpm
echo "USE_SYSTEM_FPM=true" >> $GITHUB_ENV
- name: Build App
run: yarn build --publish never ${{ steps.config.outputs.build-args }} -l ${{ inputs.targets }}
run: yarn build --publish never -l ${{ steps.config.outputs.build-args }}
env:
VARIANT_PATH: variant.json
# Only set for Nightly builds
VERSION: ${{ inputs.version }}
# Workaround for https://github.com/electron-userland/electron-builder/issues/5721
USE_HARD_LINKS: false
ED_NIGHTLY: ${{ inputs.version }}
- name: Check native libraries
run: |
@@ -192,9 +149,11 @@ jobs:
shopt -s globstar
FILES=$(file dist/**/*.node)
echo $FILES
echo "$FILES"
! echo "$FILES" | grep -v "$ARCH"
if [ grep -v "$ARCH" ]; then
exit 1
fi
LIBS=$(readelf -d dist/**/*.node | grep NEEDED)
echo "$LIBS"
@@ -214,48 +173,29 @@ jobs:
# We exclude *-unpacked as it loses permissions and the tarball contains it with correct permissions
- name: Upload Artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ inputs.artifact-prefix }}linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
name: linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
path: |
dist
!dist/*-unpacked/**
retention-days: 1
- name: Assert deb is present and valid
if: contains(inputs.targets, 'deb')
- name: Assert all required files are present
run: |
test -f ./dist/element-desktop*$ARCH.deb
DEB_LISTING=$(dpkg-deb --fsys-tarfile ./dist/element-desktop*.deb | tar -tv)
echo "deb listing: "
echo "$DEB_LISTING"
! echo "$DEB_LISTING" | grep '^h'
test -f ./dist/element-desktop*.tar.gz
env:
ARCH: ${{ inputs.arch }}
- name: Assert tar.gz is present
if: contains(inputs.targets, 'tar.gz')
run: |
test -f ./dist/element-desktop*.tar.gz
TAR_GZ_LISTING=$(tar -tvf ./dist/element-desktop*.tar.gz)
echo "tar.gz listing: "
echo "$TAR_GZ_LISTING"
! echo "$TAR_GZ_LISTING" | grep '^h'
test:
name: Test Linux ${{ inputs.arch }} SQLCipher ${{ inputs.sqlcipher }}
needs: build
if: inputs.test && contains(inputs.targets, 'deb')
uses: ./.github/workflows/build_test.yaml
with:
project: linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
artifact: ${{ inputs.artifact-prefix }}linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
runs-on: ${{ inputs.runs-on || (inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04') }}
artifact: linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
runs-on: ${{ inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04' }}
executable: /opt/Element*/element-desktop*
prepare_cmd: |
sudo apt-get -qq update
sudo apt install ./dist/*.deb
blob_report: ${{ inputs.blob_report }}
args: ${{ inputs.test-args }}

View File

@@ -15,10 +15,6 @@ on:
APPLE_CSC_LINK:
required: false
inputs:
ref:
type: string
required: false
description: "The git ref to checkout, defaults to the default branch"
version:
type: string
required: false
@@ -35,58 +31,21 @@ on:
type: boolean
required: false
description: "Whether to run the blob report"
prepare-artifact-name:
type: string
required: false
description: |
The name of the prepare artifact to use, defaults to 'webapp'.
The artifact must contain the following:
+ webapp.asar - the asar archive of the webapp to embed in the desktop app
+ electronVersion - the version of electron to use for cache keying
+ hakHash - the hash of the .hak directory to use for cache keying
+ variant.json - the variant configuration to use for the build
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
for example icons in the `build/` directory to override the app icons.
default: "webapp"
test:
type: boolean
required: false
default: true
description: "Whether to run the test stage after building"
test-args:
type: string
required: false
description: "Additional arguments to pass to playwright"
artifact-prefix:
type: string
required: false
description: "An optional prefix to add to the artifact name, useful for distinguishing builds in private repos."
default: ""
targets:
type: string
required: false
description: "List of targets to build"
default: "dmg zip"
permissions: {} # No permissions required
jobs:
build:
name: Build macOS Universal
runs-on: macos-14 # M1
environment: ${{ inputs.sign && 'packages.element.io' || '' }}
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
with:
repository: element-hq/element-desktop
ref: ${{ inputs.ref }}
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: ${{ inputs.prepare-artifact-name }}
name: webapp
- name: Cache .hak
id: cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
with:
key: ${{ runner.os }}-${{ hashFiles('hakHash', 'electronVersion') }}
path: |
@@ -101,11 +60,11 @@ jobs:
rustup target add x86_64-apple-darwin
# M1 macos-14 comes without Python preinstalled
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.13"
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: .node-version
cache: "yarn"
@@ -113,6 +72,10 @@ jobs:
- name: Install Deps
run: "yarn install --frozen-lockfile"
# Python 3.12 drops distutils which keytar relies on
- name: Install setuptools
run: pip3 install setuptools
- name: Build Natives
if: steps.cache.outputs.cache-hit != 'true'
run: yarn build:native:universal
@@ -121,16 +84,15 @@ jobs:
- name: "[Signed] Build App"
if: inputs.sign != ''
run: |
yarn build:universal --publish never -m ${{ inputs.targets }}
yarn build:universal --publish never
env:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CSC_KEY_PASSWORD }}
CSC_LINK: ${{ secrets.APPLE_CSC_LINK }}
VARIANT_PATH: variant.json
# Only set for Nightly builds
VERSION: ${{ inputs.version }}
ED_NIGHTLY: ${{ inputs.version }}
- name: Check app was signed & notarised successfully
if: inputs.sign != ''
@@ -143,10 +105,9 @@ jobs:
- name: "[Unsigned] Build App"
if: inputs.sign == ''
run: |
yarn build:universal --publish never -m ${{ inputs.targets }}
yarn build:universal --publish never
env:
CSC_IDENTITY_AUTO_DISCOVERY: false
VARIANT_PATH: variant.json
- name: Generate releases.json
if: inputs.base-url
@@ -176,32 +137,24 @@ jobs:
# We exclude mac-universal as the unpacked app takes forever to upload and zip and dmg already contains it
- name: Upload Artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ inputs.artifact-prefix }}macos
name: macos
path: |
dist
!dist/mac-universal/**
retention-days: 1
- name: Assert zip is present
if: contains(inputs.targets, 'zip')
run: |
test -f ./dist/Element*-mac.zip
- name: Assert dmg is present
if: contains(inputs.targets, 'dmg')
- name: Assert all required files are present
run: |
test -f ./dist/Element*.dmg
test -f ./dist/Element*-mac.zip
test:
name: Test macOS Universal
needs: build
if: inputs.test && contains(inputs.targets, 'dmg')
uses: ./.github/workflows/build_test.yaml
with:
project: macos
artifact: ${{ inputs.artifact-prefix }}macos
artifact: macos
runs-on: macos-14
executable: /Users/runner/Applications/Element*.app/Contents/MacOS/Element*
# We need to mount the DMG and copy the app to the Applications folder as a mounted DMG is
@@ -211,4 +164,3 @@ jobs:
rsync -a /Volumes/Element/Element*.app ~/Applications/ &&
hdiutil detach /Volumes/Element
blob_report: ${{ inputs.blob_report }}
args: ${{ inputs.test-args }}

View File

@@ -54,9 +54,9 @@ jobs:
outputs:
nightly-version: ${{ steps.versions.outputs.nightly }}
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: .node-version
cache: "yarn"
@@ -85,11 +85,6 @@ jobs:
if: steps.branch-matching.outcome == 'failure' || steps.branch-matching.outcome == 'skipped'
run: yarn run fetch --noverify -d ${{ inputs.config }} ${{ inputs.version }}
- name: Copy variant config
run: cp "$CONFIG_DIR/build.json" variant.json
env:
CONFIG_DIR: ${{ inputs.config }}
# We split this out to save the build_* scripts having to do it to make use of `hashFiles` in the cache action
- name: Generate cache hash files
run: |
@@ -165,13 +160,13 @@ jobs:
echo "| Element Web | [$WEB_VERSION](https://github.com/element-hq/element-web/commit/$WEB_VERSION) |" >> $GITHUB_STEP_SUMMARY
echo "| JS SDK | [$JS_VERSION](https://github.com/matrix-org/matrix-js-sdk/commit/$JS_VERSION) |" >> $GITHUB_STEP_SUMMARY
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: webapp
retention-days: 1
path: |
webapp.asar
package.json
electronVersion
hakHash
changelog.Debian
variant.json

View File

@@ -10,10 +10,6 @@ on:
type: string
required: true
description: "The name of the artifact to download"
project:
type: string
required: true
description: "The Playwright project to use for testing"
executable:
type: string
required: true
@@ -26,21 +22,14 @@ on:
type: boolean
default: false
description: "Whether to upload a blob report instead of the HTML report"
args:
type: string
required: false
description: "Additional arguments to pass to playwright, for e.g. skipping specific tests"
permissions: {}
jobs:
test:
name: Test ${{ inputs.project }}
runs-on: ${{ inputs.runs-on }}
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
with:
repository: ${{ github.repository == 'element-hq/element-web-pro' && 'element-hq/element-desktop' || github.repository }}
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: .node-version
cache: "yarn"
@@ -48,7 +37,7 @@ jobs:
- name: Install Deps
run: "yarn install --frozen-lockfile"
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: ${{ inputs.artifact }}
path: dist
@@ -80,13 +69,13 @@ jobs:
uses: coactions/setup-xvfb@6b00cf1889f4e1d5a48635647013c0508128ee1a
timeout-minutes: 20
with:
run: yarn test --project=${{ inputs.project }} ${{ runner.os != 'Linux' && '--ignore-snapshots' || '' }} ${{ inputs.blob_report == false && '--reporter=html' || '' }} ${{ inputs.args }}
run: yarn test --project=${{ inputs.artifact }} ${{ runner.os != 'Linux' && '--ignore-snapshots' || '' }} ${{ inputs.blob_report == false && '--reporter=html' || '' }}
env:
ELEMENT_DESKTOP_EXECUTABLE: ${{ steps.executable.outputs.path }}
- name: Upload blob report
if: always() && inputs.blob_report
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: blob-report-${{ inputs.artifact }}
path: blob-report
@@ -94,7 +83,7 @@ jobs:
- name: Upload HTML report
if: always() && inputs.blob_report == false
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ inputs.artifact }}-test
path: playwright-report

View File

@@ -18,10 +18,6 @@ on:
ESIGNER_USER_TOTP:
required: false
inputs:
ref:
type: string
required: false
description: "The git ref to checkout, defaults to the default branch"
arch:
type: string
required: true
@@ -38,48 +34,9 @@ on:
type: boolean
required: false
description: "Whether to run the blob report"
prepare-artifact-name:
type: string
required: false
description: |
The name of the prepare artifact to use, defaults to 'webapp'.
The artifact must contain the following:
+ webapp.asar - the asar archive of the webapp to embed in the desktop app
+ electronVersion - the version of electron to use for cache keying
+ hakHash - the hash of the .hak directory to use for cache keying
+ variant.json - the variant configuration to use for the build
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
for example icons in the `build/` directory to override the app icons.
default: "webapp"
test:
type: boolean
required: false
default: true
description: "Whether to run the test stage after building"
test-runs-on:
type: string
required: false
description: "The runner image to use for testing, normally set for you, may be needed for running in private repos."
test-args:
type: string
required: false
description: "Additional arguments to pass to playwright"
artifact-prefix:
type: string
required: false
description: "An optional prefix to add to the artifact name, useful for distinguishing builds in private repos."
default: ""
targets:
type: string
required: false
description: "List of targets to build"
default: "squirrel msi"
permissions: {} # No permissions required
jobs:
build:
name: Build Windows ${{ inputs.arch }}
runs-on: windows-2025
environment: ${{ inputs.sign && 'packages.element.io' || '' }}
env:
@@ -108,18 +65,15 @@ jobs:
}
}
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
with:
repository: element-hq/element-desktop
ref: ${{ inputs.ref }}
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: ${{ inputs.prepare-artifact-name }}
name: webapp
- name: Cache .hak
id: cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4
with:
key: ${{ runner.os }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion') }}
path: |
@@ -148,7 +102,7 @@ jobs:
rustup default stable
rustup target add ${{ steps.config.outputs.target }}
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: .node-version
cache: "yarn"
@@ -232,16 +186,10 @@ jobs:
MASTER_KEY_FILE: C:\Users\runneradmin\eSignerCKA\master.key
- name: Build App
run: yarn build --publish never ${{ steps.config.outputs.build-args }} -w ${{ inputs.targets }}
run: yarn build --publish never -w ${{ steps.config.outputs.build-args }}
env:
VARIANT_PATH: variant.json
# Only set for Nightly builds
# The windows packager relies on parsing this as semver, so we have to make it look like one.
# This will give our update packages really stupid names, but we probably can't change that either
# because squirrel windows parses them for the version too. We don't really care: nobody sees them.
# We just give the installer a static name, so you'll just see this in the 'about' dialog.
# Turns out if you use 0.0.0 here it makes Squirrel windows crash, so we use 0.0.1.
VERSION: ${{ inputs.version && format('0.0.1-nightly.{0}', inputs.version) || '' }}
ED_NIGHTLY: ${{ inputs.version }}
- name: Trust eSigner sandbox cert
if: inputs.sign == ''
@@ -258,38 +206,26 @@ jobs:
| ForEach-Object -Process {. $env:SIGNTOOL_PATH verify /pa $_.FullName; if(!$?) { throw }}
- name: Upload Artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ inputs.artifact-prefix }}win-${{ inputs.arch }}
name: win-${{ inputs.arch }}
path: |
dist
retention-days: 1
- name: Assert executable is present
- name: Assert all required files are present
run: |
Test-Path './dist/win-*unpacked/Element*.exe'
- name: Assert all Squirrel files are present
if: contains(inputs.targets, 'squirrel')
run: |
Test-Path './dist/squirrel-windows*/Element Setup*.exe'
Test-Path './dist/squirrel-windows*/element-desktop-*-full.nupkg'
Test-Path './dist/squirrel-windows*/RELEASES'
- name: Assert MSI is present
if: contains(inputs.targets, 'msi')
run: |
Test-Path './dist/Element*.msi'
test:
name: Test Windows ${{ inputs.arch }}
needs: build
if: inputs.test
uses: ./.github/workflows/build_test.yaml
with:
project: win-${{ inputs.arch }}
artifact: ${{ inputs.artifact-prefix }}win-${{ inputs.arch }}
runs-on: ${{ inputs.test-runs-on || (inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2022') }}
artifact: win-${{ inputs.arch }}
runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2022' }}
executable: ./dist/win*-unpacked/Element*.exe
blob_report: ${{ inputs.blob_report }}
args: ${{ inputs.test-args }}

View File

@@ -19,18 +19,18 @@ jobs:
contents: read
packages: write
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3
with:
install: true
- name: Build test image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
with:
file: dockerbuild/Dockerfile
push: false
@@ -42,7 +42,7 @@ jobs:
run: docker run -v $PWD:/project element-desktop-dockerbuild yarn install
- name: Log in to the Container registry
uses: docker/login-action@28fdb31ff34708d19615a74d67103ddc2ea9725c
uses: docker/login-action@6d4b68b490aef8836e8fb5e50ee7b3bdfa5894f0
if: github.event_name != 'pull_request'
with:
registry: ${{ env.REGISTRY }}
@@ -52,7 +52,7 @@ jobs:
- name: Extract metadata for Docker
id: meta
if: github.event_name != 'pull_request'
uses: docker/metadata-action@318604b99e75e41977312d83839a89be02ca4893 # v5
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
@@ -61,7 +61,7 @@ jobs:
- name: Build and push Docker image
if: github.event_name != 'pull_request'
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6
with:
file: dockerbuild/Dockerfile
push: true

View File

@@ -9,9 +9,9 @@ jobs:
name: "Typescript Syntax Check"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: package.json
cache: "yarn"
@@ -35,9 +35,9 @@ jobs:
name: "ESLint"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: package.json
cache: "yarn"
@@ -53,9 +53,9 @@ jobs:
name: "Workflow Lint"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: package.json
cache: "yarn"
@@ -71,9 +71,9 @@ jobs:
name: "Analyse Dead Code"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version-file: package.json
cache: "yarn"

View File

@@ -12,7 +12,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9
with:
operations-per-run: 250
days-before-issue-stale: -1

1
.gitignore vendored
View File

@@ -18,4 +18,3 @@ yarn-error.log
/hak/**/*.js
/scripts/hak/**/*.js
.DS_Store
/playwright-report

View File

@@ -1 +1 @@
24.11.1
v22.15.0

View File

@@ -1,489 +1,3 @@
Changes in [1.12.7](https://github.com/element-hq/element-desktop/releases/tag/v1.12.7) (2025-12-16)
====================================================================================================
## ✨ Features
* Replace legacy icons with compound ([#31424](https://github.com/element-hq/element-web/pull/31424)). Contributed by @t3chguy.
* Update polls UX to match EX Mobile and improve accessibility ([#31245](https://github.com/element-hq/element-web/pull/31245)). Contributed by @langleyd.
* Add option to enable read receipt and marker when user interact with UI ([#31353](https://github.com/element-hq/element-web/pull/31353)). Contributed by @florianduros.
* Introduce a hook to auto dispose view models ([#31178](https://github.com/element-hq/element-web/pull/31178)). Contributed by @MidhunSureshR.
* Update settings toggles to use consistent design across app. ([#30169](https://github.com/element-hq/element-web/pull/30169)). Contributed by @Half-Shot.
* Add ability to the room view to hide widgets ([#31400](https://github.com/element-hq/element-web/pull/31400)). Contributed by @langleyd.
* call: Pass the echo cancellation and noise suppression settings to EC ([#31317](https://github.com/element-hq/element-web/pull/31317)). Contributed by @BillCarsonFr.
* Tweak rendering of icons for a11y ([#31358](https://github.com/element-hq/element-web/pull/31358)). Contributed by @t3chguy.
* Implement new `renderNotificationDecoration` from module API ([#31389](https://github.com/element-hq/element-web/pull/31389)). Contributed by @MidhunSureshR.
* Replace more icons with compound ([#31381](https://github.com/element-hq/element-web/pull/31381)). Contributed by @t3chguy.
* Replace more icons with compound ([#31378](https://github.com/element-hq/element-web/pull/31378)). Contributed by @t3chguy.
* `<Banner/>`: Hide `Dismiss` button if `onClose` handler is not provided. ([#31362](https://github.com/element-hq/element-web/pull/31362)). Contributed by @kaylendog.
* Replace batch of legacy icons with compound design tokens ([#31360](https://github.com/element-hq/element-web/pull/31360)). Contributed by @t3chguy.
* MSC4380: Invite blocking ([#31268](https://github.com/element-hq/element-web/pull/31268)). Contributed by @richvdh.
* Tweak rendering of icons for accessibility ([#31346](https://github.com/element-hq/element-web/pull/31346)). Contributed by @t3chguy.
* Implement a shared `Banner` component. ([#31266](https://github.com/element-hq/element-web/pull/31266)). Contributed by @kaylendog.
* Allow the Login screen to use the dark theme ([#31293](https://github.com/element-hq/element-web/pull/31293)). Contributed by @richvdh.
## 🐛 Bug Fixes
* [Backport staging] Amend e2e normal icon from lock-solid to info ([#31559](https://github.com/element-hq/element-web/pull/31559)). Contributed by @t3chguy.
* [Backport staging] Fix CSS specificity causing icon issues in e2e verification ([#31548](https://github.com/element-hq/element-web/pull/31548)). Contributed by @RiotRobot.
* [Backport staging] Fix e2e icons in CompleteSecurity \& SetupEncryptionBody ([#31522](https://github.com/element-hq/element-web/pull/31522)). Contributed by @RiotRobot.
* [Backport staging] Remove an extra paragraph in advanced room settings ([#31511](https://github.com/element-hq/element-web/pull/31511)). Contributed by @RiotRobot.
* [Backport staging] Don't show the key storage out of sync toast when backup disabled ([#31507](https://github.com/element-hq/element-web/pull/31507)). Contributed by @RiotRobot.
* Fix composer button visibility in contrast colour mode ([#31255](https://github.com/element-hq/element-web/pull/31255)). Contributed by @t3chguy.
* Ensure correct room version is used and permissions are appropriately sert when creating rooms ([#31464](https://github.com/element-hq/element-web/pull/31464)). Contributed by @Half-Shot.
* Fix e2e icon rendering ([#31454](https://github.com/element-hq/element-web/pull/31454)). Contributed by @t3chguy.
* EventIndexer: ensure we add initial checkpoints when the db is first opened ([#31448](https://github.com/element-hq/element-web/pull/31448)). Contributed by @richvdh.
* Fix `/join <alias>` command failing due to race condition ([#31433](https://github.com/element-hq/element-web/pull/31433)). Contributed by @MidhunSureshR.
* MessageEventIndexDialog: distinguish indexed rooms ([#31436](https://github.com/element-hq/element-web/pull/31436)). Contributed by @richvdh.
* Move `EditInPlace` out of `Form` (Fixes: reloading EW on EC url update) ([#31434](https://github.com/element-hq/element-web/pull/31434)). Contributed by @toger5.
* Fixes issue where cursor would jump to the beginning of the input field after converting Japanese text and pressing Tab ([#31432](https://github.com/element-hq/element-web/pull/31432)). Contributed by @shinaoka.
* Fix widgets getting stuck in loading states ([#31314](https://github.com/element-hq/element-web/pull/31314)). Contributed by @robintown.
* Room list: fix room options remaining on room item after mouse leaving ([#31414](https://github.com/element-hq/element-web/pull/31414)). Contributed by @florianduros.
* Make `RoomList.showMessagePreview` configurable by `config.json` ([#31419](https://github.com/element-hq/element-web/pull/31419)). Contributed by @florianduros.
* Fix bug which caused app not to load correctly when `force_verification` is enabled ([#31265](https://github.com/element-hq/element-web/pull/31265)). Contributed by @richvdh.
* Room list: display the menu option on the room list item when clicked/opened ([#31380](https://github.com/element-hq/element-web/pull/31380)). Contributed by @florianduros.
* Fix handling of SVGs ([#31359](https://github.com/element-hq/element-web/pull/31359)). Contributed by @t3chguy.
* Fix word wrapping in expanded left panel buttons ([#31377](https://github.com/element-hq/element-web/pull/31377)). Contributed by @t3chguy.
* Fix aspect ratio on error view background ([#31361](https://github.com/element-hq/element-web/pull/31361)). Contributed by @t3chguy.
* Fix failure to request persistent storage perms ([#31299](https://github.com/element-hq/element-web/pull/31299)). Contributed by @richvdh.
* Fix calls sometimes not knowing that they're presented ([#31313](https://github.com/element-hq/element-web/pull/31313)). Contributed by @robintown.
Changes in [1.12.6](https://github.com/element-hq/element-desktop/releases/tag/v1.12.6) (2025-12-03)
====================================================================================================
This release fixes a bug where 1:1 calling was incorrectly not available if no Element Call focus was set.
## 🐛 Bug Fixes
* Add option to pick call options for voice calls. ([#31413](https://github.com/element-hq/element-web/pull/31413)).
Changes in [1.12.5](https://github.com/element-hq/element-desktop/releases/tag/v1.12.5) (2025-12-02)
====================================================================================================
## ✨ Features
* Update Emojibase to v17 ([#31307](https://github.com/element-hq/element-web/pull/31307)). Contributed by @t3chguy.
* Adds tooltip for compose menu ([#31122](https://github.com/element-hq/element-web/pull/31122)). Contributed by @byteplow.
* Add option to hide pinned message banner in room view ([#31296](https://github.com/element-hq/element-web/pull/31296)). Contributed by @florianduros.
* update twemoji to not monochromise emoji with BLACK in their name ([#31281](https://github.com/element-hq/element-web/pull/31281)). Contributed by @ara4n.
* upgrade to twemoji 17.0.2 and fix #14695 ([#31267](https://github.com/element-hq/element-web/pull/31267)). Contributed by @ara4n.
* Add options to hide right panel in room view ([#31252](https://github.com/element-hq/element-web/pull/31252)). Contributed by @florianduros.
* Delayed event management: split endpoints, no auth ([#31183](https://github.com/element-hq/element-web/pull/31183)). Contributed by @AndrewFerr.
* Support using Element Call for voice calls in DMs ([#30817](https://github.com/element-hq/element-web/pull/30817)). Contributed by @Half-Shot.
* Improve screen reader accessibility of auth pages ([#31236](https://github.com/element-hq/element-web/pull/31236)). Contributed by @t3chguy.
* Add posthog tracking for key backup toasts ([#31195](https://github.com/element-hq/element-web/pull/31195)). Contributed by @Half-Shot.
## 🐛 Bug Fixes
* Return to using Fira Code as the default monospace font ([#31302](https://github.com/element-hq/element-web/pull/31302)). Contributed by @ara4n.
* Fix case of home screen being displayed erroneously ([#31301](https://github.com/element-hq/element-web/pull/31301)). Contributed by @langleyd.
* Fix message edition and reply when multiple rooms at displayed the same moment ([#31280](https://github.com/element-hq/element-web/pull/31280)). Contributed by @florianduros.
* Key storage out of sync: reset key backup when needed ([#31279](https://github.com/element-hq/element-web/pull/31279)). Contributed by @uhoreg.
* Fix invalid events crashing entire room rather than just their tile ([#31256](https://github.com/element-hq/element-web/pull/31256)). Contributed by @t3chguy.
* Fix expand button of space panel getting cut off at the edges ([#31259](https://github.com/element-hq/element-web/pull/31259)). Contributed by @MidhunSureshR.
* Fix pill buttons in dialogs ([#31246](https://github.com/element-hq/element-web/pull/31246)). Contributed by @dbkr.
* Fix blank sections at the top and bottom of the member list when scrolling ([#31198](https://github.com/element-hq/element-web/pull/31198)). Contributed by @langleyd.
* Fix emoji category selection with keyboard ([#31162](https://github.com/element-hq/element-web/pull/31162)). Contributed by @langleyd.
Changes in [1.12.4](https://github.com/element-hq/element-desktop/releases/tag/v1.12.4) (2025-11-18)
====================================================================================================
## ✨ Features
* Update nightly and release builds to use the dedicated subdomain for 'bug\_report\_endpoint\_url' ([#2677](https://github.com/element-hq/element-desktop/pull/2677)). Contributed by @benbz.
* Apply aria-hidden to emoji in SAS verification ([#31204](https://github.com/element-hq/element-web/pull/31204)). Contributed by @t3chguy.
* Add options to hide header and composer of room view for the module api ([#31095](https://github.com/element-hq/element-web/pull/31095)). Contributed by @florianduros.
* Experimental Module API Additions ([#30863](https://github.com/element-hq/element-web/pull/30863)). Contributed by @dbkr.
* Change polls to use fieldset/legend markup ([#31160](https://github.com/element-hq/element-web/pull/31160)). Contributed by @langleyd.
* Use compound Button styles for Jitsi button ([#31159](https://github.com/element-hq/element-web/pull/31159)). Contributed by @Half-Shot.
* Add FocusLock to emoji picker ([#31146](https://github.com/element-hq/element-web/pull/31146)). Contributed by @langleyd.
* Move room name, avatar, and topic to IOpts. ([#30981](https://github.com/element-hq/element-web/pull/30981)). Contributed by @kaylendog.
* Add a devtool for looking at users and their devices ([#30983](https://github.com/element-hq/element-web/pull/30983)). Contributed by @uhoreg.
## 🐛 Bug Fixes
* Fix room list handling of membership changes ([#31197](https://github.com/element-hq/element-web/pull/31197)). Contributed by @t3chguy.
* Fix room list unable to be resized when displayed after a module ([#31186](https://github.com/element-hq/element-web/pull/31186)). Contributed by @florianduros.
* Inhibit keyboard highlights in dialogs when effector is not in focus ([#31181](https://github.com/element-hq/element-web/pull/31181)). Contributed by @t3chguy.
* Strip mentions from forwarded messages ([#30884](https://github.com/element-hq/element-web/pull/30884)). Contributed by @twassman.
* Don't allow pin or edit of messages with a send status ([#31158](https://github.com/element-hq/element-web/pull/31158)). Contributed by @langleyd.
* Hide room header buttons if the room hasn't been created yet. ([#31092](https://github.com/element-hq/element-web/pull/31092)). Contributed by @Half-Shot.
* Fix screen readers not indicating the emoji picker search field is focused. ([#31128](https://github.com/element-hq/element-web/pull/31128)). Contributed by @langleyd.
* Fix emoji picker highlight missing when not active element ([#31148](https://github.com/element-hq/element-web/pull/31148)). Contributed by @t3chguy.
* Add relevant aria attribute for selected emoji in the emoji picker ([#31125](https://github.com/element-hq/element-web/pull/31125)). Contributed by @t3chguy.
* Fix tooltips within context menu portals being unreliable ([#31129](https://github.com/element-hq/element-web/pull/31129)). Contributed by @t3chguy.
* Avoid excessive re-render of room list and member list ([#31131](https://github.com/element-hq/element-web/pull/31131)). Contributed by @florianduros.
* Make emoji picker height responsive. ([#31130](https://github.com/element-hq/element-web/pull/31130)). Contributed by @langleyd.
* Emoji Picker: Focused emoji does not move with the arrow keys ([#30893](https://github.com/element-hq/element-web/pull/30893)). Contributed by @langleyd.
* Fix audio player seek bar position ([#31127](https://github.com/element-hq/element-web/pull/31127)). Contributed by @florianduros.
* Add aria label to emoji picker search ([#31126](https://github.com/element-hq/element-web/pull/31126)). Contributed by @langleyd.
Changes in [1.12.3](https://github.com/element-hq/element-desktop/releases/tag/v1.12.3) (2025-11-04)
====================================================================================================
## 🦖 Deprecations
* Remove allowVoipWithNoMedia feature flag ([#31087](https://github.com/element-hq/element-web/pull/31087)). Contributed by @Half-Shot.
## ✨ Features
* Change module API to be an instance getter ([#31025](https://github.com/element-hq/element-web/pull/31025)). Contributed by @dbkr.
## 🐛 Bug Fixes
* Wait for Electron to be ready before we fire syntax error dialog ([#2659](https://github.com/element-hq/element-desktop/pull/2659)). Contributed by @t3chguy.
* Show hover elements when keyboard focus is within an event tile ([#31078](https://github.com/element-hq/element-web/pull/31078)). Contributed by @t3chguy.
* Ensure toolbar navigation pattern works in MessageActionBar ([#31080](https://github.com/element-hq/element-web/pull/31080)). Contributed by @t3chguy.
* Ensure sent markers are hidden when showing thread summary. ([#31076](https://github.com/element-hq/element-web/pull/31076)). Contributed by @Half-Shot.
* Fix translation in dev mode ([#31045](https://github.com/element-hq/element-web/pull/31045)). Contributed by @florianduros.
* Fix sort order in space hierarchy ([#30975](https://github.com/element-hq/element-web/pull/30975)). Contributed by @t3chguy.
* New Room list: don't display message preview of thread ([#31043](https://github.com/element-hq/element-web/pull/31043)). Contributed by @florianduros.
* Revert "A11y: move focus to right panel when opened" ([#30999](https://github.com/element-hq/element-web/pull/30999)). Contributed by @florianduros.
* Fix highlights in messages (or search results) breaking links ([#30264](https://github.com/element-hq/element-web/pull/30264)). Contributed by @bojidar-bg.
* Add prepare script ([#31030](https://github.com/element-hq/element-web/pull/31030)). Contributed by @dbkr.
* Fix html exports by adding SDKContext ([#30987](https://github.com/element-hq/element-web/pull/30987)). Contributed by @t3chguy.
Changes in [1.12.2](https://github.com/element-hq/element-desktop/releases/tag/v1.12.2) (2025-10-21)
====================================================================================================
## ✨ Features
* Allow Desktop app to be auto-started minimised or focused ([#2622](https://github.com/element-hq/element-desktop/pull/2622)). Contributed by @t3chguy.
* Room List: Extend the viewport to avoid so many black spots when scrolling the room list ([#30867](https://github.com/element-hq/element-web/pull/30867)). Contributed by @langleyd.
* Hide calling buttons in room header before a room is created ([#30816](https://github.com/element-hq/element-web/pull/30816)). Contributed by @Half-Shot.
* Improve invite dialog ui - Part 2 ([#30836](https://github.com/element-hq/element-web/pull/30836)). Contributed by @florianduros.
## 🐛 Bug Fixes
* Fix hardlinks appearing in and breaking deb packages ([#2609](https://github.com/element-hq/element-desktop/pull/2609)). Contributed by @t3chguy.
* Fix platform settings race condition and make auto-launch tri-state ([#30977](https://github.com/element-hq/element-web/pull/30977)). Contributed by @t3chguy.
* Fix: member count in header and member list ([#30982](https://github.com/element-hq/element-web/pull/30982)). Contributed by @florianduros.
* Fix duration of voice message in timeline ([#30973](https://github.com/element-hq/element-web/pull/30973)). Contributed by @florianduros.
* Fix voice notes rendering at 00:00 when playback had not begun. ([#30961](https://github.com/element-hq/element-web/pull/30961)). Contributed by @Half-Shot.
* Improve handling of animated images, add support for AVIF animations ([#30932](https://github.com/element-hq/element-web/pull/30932)). Contributed by @t3chguy.
* Update key storage toggle when key storage status changes ([#30934](https://github.com/element-hq/element-web/pull/30934)). Contributed by @uhoreg.
* Fix jitsi widget popout ([#30908](https://github.com/element-hq/element-web/pull/30908)). Contributed by @dbkr.
* Improve keyboard navigation on invite dialog ([#30930](https://github.com/element-hq/element-web/pull/30930)). Contributed by @florianduros.
* Prefer UIA flows with supported UIA stages ([#30926](https://github.com/element-hq/element-web/pull/30926)). Contributed by @richvdh.
* Enhance accessibility of dropdown ([#30928](https://github.com/element-hq/element-web/pull/30928)). Contributed by @florianduros.
* Improve accessibility of the `\<AvatarSetting> component ([#30907](https://github.com/element-hq/element-web/pull/30907)). Contributed by @MidhunSureshR.
Changes in [1.12.1](https://github.com/element-hq/element-desktop/releases/tag/v1.12.1) (2025-10-07)
====================================================================================================
## ✨ Features
* New Room List: Change the order of filters to match those on mobile ([#30905](https://github.com/element-hq/element-web/pull/30905)). Contributed by @langleyd.
* New Room List: Don't clear filters on space change ([#30903](https://github.com/element-hq/element-web/pull/30903)). Contributed by @langleyd.
* Add release announcement for the sounds ([#30900](https://github.com/element-hq/element-web/pull/30900)). Contributed by @langleyd.
* Rich Text Editor: Add emoji suggestion support ([#30873](https://github.com/element-hq/element-web/pull/30873)). Contributed by @langleyd.
* feat: Disable session lock when running in element-desktop ([#30643](https://github.com/element-hq/element-web/pull/30643)). Contributed by @kaylendog.
* Improve invite dialog ui - Part 1 ([#30764](https://github.com/element-hq/element-web/pull/30764)). Contributed by @florianduros.
* Update Message Sound for Element ([#30804](https://github.com/element-hq/element-web/pull/30804)). Contributed by @beatdemon.
* Add new and improved ringtone ([#30761](https://github.com/element-hq/element-web/pull/30761)). Contributed by @Half-Shot.
* Disable RTE formatting buttons when the content contains a slash command ([#30802](https://github.com/element-hq/element-web/pull/30802)). Contributed by @langleyd.
## 🐛 Bug Fixes
* New Room List: Improve robustness of keyboard navigation ([#30888](https://github.com/element-hq/element-web/pull/30888)). Contributed by @langleyd.
* Fix a11y issue on list in invite dialog ([#30878](https://github.com/element-hq/element-web/pull/30878)). Contributed by @florianduros.
* Switch Export and Import Icons to match intuition ([#30805](https://github.com/element-hq/element-web/pull/30805)). Contributed by @micartey.
* Hide breadcrumb option when new room list is enabled ([#30869](https://github.com/element-hq/element-web/pull/30869)). Contributed by @florianduros.
* Avoid creating multiple call objects for the same widget ([#30839](https://github.com/element-hq/element-web/pull/30839)). Contributed by @robintown.
* Add a test for #29882, which is fixed by matrix-org/matrix-js-sdk#5016 ([#30835](https://github.com/element-hq/element-web/pull/30835)). Contributed by @andybalaam.
* fix: use `help_encryption_url` of config instead of hardcoded `https://element.io/help#encryption5` ([#30746](https://github.com/element-hq/element-web/pull/30746)). Contributed by @florianduros.
* Fix html export when feature\_jump\_to\_date is enabled ([#30828](https://github.com/element-hq/element-web/pull/30828)). Contributed by @langleyd.
* Fix #30439: "Forgot recovery key" should go to "reset" ([#30771](https://github.com/element-hq/element-web/pull/30771)). Contributed by @andybalaam.
Changes in [1.12.0](https://github.com/element-hq/element-desktop/releases/tag/v1.12.0) (2025-09-23)
====================================================================================================
## 🦖 Deprecations
* Remove remaining support for outdated .well-known settings ([#30702](https://github.com/element-hq/element-web/pull/30702)). Contributed by @richvdh.
## ✨ Features
* Automatically select first source for desktop capture under Wayland ([#2526](https://github.com/element-hq/element-desktop/pull/2526)). Contributed by @byquanton.
* Add decline button to call notification toast (use new notification event) ([#30729](https://github.com/element-hq/element-web/pull/30729)). Contributed by @toger5.
* Use the new room list by default ([#30640](https://github.com/element-hq/element-web/pull/30640)). Contributed by @langleyd.
* "Verify this device" redesign ([#30596](https://github.com/element-hq/element-web/pull/30596)). Contributed by @uhoreg.
* Set Element Call "intents" when starting and answering DM calls. ([#30730](https://github.com/element-hq/element-web/pull/30730)). Contributed by @Half-Shot.
* Add axe compliance for new room list ([#30700](https://github.com/element-hq/element-web/pull/30700)). Contributed by @langleyd.
* Stop ringing and remove toast if another device answers a RTC call. ([#30728](https://github.com/element-hq/element-web/pull/30728)). Contributed by @Half-Shot.
* Automatically adjust history visibility when making a room private ([#30713](https://github.com/element-hq/element-web/pull/30713)). Contributed by @Half-Shot.
* Release announcement for new room list ([#30675](https://github.com/element-hq/element-web/pull/30675)). Contributed by @dbkr.
## 🐛 Bug Fixes
* Update Electron to v38.1.0 to fix Kernel crash on multi-GPU systems ([#2544](https://github.com/element-hq/element-desktop/pull/2544)). Contributed by @Arcitec.
* Fix Confirm your identity buttons being unclickable ([#2554](https://github.com/element-hq/element-desktop/pull/2554)). Contributed by @dbkr.
* Ensure dropdown is not a drag element on macOS ([#2540](https://github.com/element-hq/element-desktop/pull/2540)). Contributed by @t3chguy.
* [Backport staging] Room list: make the filter resize correctly ([#30795](https://github.com/element-hq/element-web/pull/30795)). Contributed by @RiotRobot.
* [Backport staging] Avoid flicker of the room list filter on resize ([#30794](https://github.com/element-hq/element-web/pull/30794)). Contributed by @RiotRobot.
* Don't show release announcements while toasts are displayed ([#30770](https://github.com/element-hq/element-web/pull/30770)). Contributed by @dbkr.
* Fix enabling key backup not working if there is an untrusted key backup ([#30707](https://github.com/element-hq/element-web/pull/30707)). Contributed by @Half-Shot.
* Force `preload` to be false when setting an intent on an Element Call. ([#30759](https://github.com/element-hq/element-web/pull/30759)). Contributed by @Half-Shot.
* Fix handling of 413 server response when uploading media ([#30737](https://github.com/element-hq/element-web/pull/30737)). Contributed by @hughns.
* Make landmark navigation work with new room list ([#30747](https://github.com/element-hq/element-web/pull/30747)). Contributed by @dbkr.
* Prevent voice message from displaying spurious errors ([#30736](https://github.com/element-hq/element-web/pull/30736)). Contributed by @florianduros.
* Align default avatar and fix colors in composer pills ([#30739](https://github.com/element-hq/element-web/pull/30739)). Contributed by @florianduros.
* Use configured URL for link to desktop app in message search settings ([#30742](https://github.com/element-hq/element-web/pull/30742)). Contributed by @t3chguy.
* Fix history visibility when creating space rooms ([#30745](https://github.com/element-hq/element-web/pull/30745)). Contributed by @dbkr.
* Check HTML-encoded quotes when handling translations for embedded pages (such as welcome.html) ([#30743](https://github.com/element-hq/element-web/pull/30743)). Contributed by @Half-Shot.
* Fix local room encryption status always not enabled ([#30461](https://github.com/element-hq/element-web/pull/30461)). Contributed by @BillCarsonFr.
* fix: make url in topic in room intro clickable ([#30686](https://github.com/element-hq/element-web/pull/30686)). Contributed by @florianduros.
* Block change recovery key button while a change is ongoing. ([#30664](https://github.com/element-hq/element-web/pull/30664)). Contributed by @Half-Shot.
* Hide advanced settings during room creation when `UIFeature.advancedSettings=false` ([#30684](https://github.com/element-hq/element-web/pull/30684)). Contributed by @florianduros.
* A11y: improve accessibility of pinned messages ([#30558](https://github.com/element-hq/element-web/pull/30558)). Contributed by @florianduros.
Changes in [1.11.112](https://github.com/element-hq/element-desktop/releases/tag/v1.11.112) (2025-09-16)
========================================================================================================
Fix [CVE-2025-59161](https://www.cve.org/CVERecord?id=CVE-2025-59161) / [GHSA-m6c8-98f4-75rr](https://github.com/element-hq/element-web/security/advisories/GHSA-m6c8-98f4-75rr)
## This is the last release compatible with macOS Big Sur. It will not update further. Big Sur is End of Life for almost 2 years and the next version of Electron crashes upon startup on Big Sur.
## ✨ Features
* [Backport staging] Handle unsupported macOS versions better ([#2555](https://github.com/element-hq/element-desktop/pull/2555)). Contributed by @RiotRobot.
Changes in [1.11.111](https://github.com/element-hq/element-desktop/releases/tag/v1.11.111) (2025-09-10)
========================================================================================================
## ✨ Features
* Do not hide media from your own user by default ([#29797](https://github.com/element-hq/element-web/pull/29797)). Contributed by @Half-Shot.
* Remember whether sidebar is shown for calls when switching rooms ([#30262](https://github.com/element-hq/element-web/pull/30262)). Contributed by @bojidar-bg.
* Open the proper integration settings on integrations disabled error ([#30538](https://github.com/element-hq/element-web/pull/30538)). Contributed by @Half-Shot.
* Show a "progress" dialog while invites are being sent ([#30561](https://github.com/element-hq/element-web/pull/30561)). Contributed by @richvdh.
* Move the room list to the new ListView(backed by react-virtuoso) ([#30515](https://github.com/element-hq/element-web/pull/30515)). Contributed by @langleyd.
## 🐛 Bug Fixes
* [Backport staging] Ensure container starts if it is mounted with an empty /modules directory. ([#30705](https://github.com/element-hq/element-web/pull/30705)). Contributed by @RiotRobot.
* Fix room joining over federation not specifying vias or using aliases ([#30641](https://github.com/element-hq/element-web/pull/30641)). Contributed by @t3chguy.
* Fix stable-suffixed MSC4133 support ([#30649](https://github.com/element-hq/element-web/pull/30649)). Contributed by @dbkr.
* Fix i18n of message when a setting is disabled ([#30646](https://github.com/element-hq/element-web/pull/30646)). Contributed by @dbkr.
* ListView should not handle the arrow keys if there is a modifier applied ([#30633](https://github.com/element-hq/element-web/pull/30633)). Contributed by @langleyd.
* Make BaseDialog's div keyboard focusable and fix test. ([#30631](https://github.com/element-hq/element-web/pull/30631)). Contributed by @langleyd.
* Fix: Allow triple-click text selection to flow around pills ([#30349](https://github.com/element-hq/element-web/pull/30349)). Contributed by @AlirezaMrtz.
* Watch for a 'join' action to know when the call is connected ([#29492](https://github.com/element-hq/element-web/pull/29492)). Contributed by @robintown.
* Fix: add missing tooltip and aria-label to lock icon next to composer ([#30623](https://github.com/element-hq/element-web/pull/30623)). Contributed by @florianduros.
* Don't render context menu when scrolling ([#30613](https://github.com/element-hq/element-web/pull/30613)). Contributed by @langleyd.
Changes in [1.11.110](https://github.com/element-hq/element-desktop/releases/tag/v1.11.110) (2025-08-27)
========================================================================================================
## ✨ Features
* Hide recovery key when re-entering it while creating or changing it ([#30499](https://github.com/element-hq/element-web/pull/30499)). Contributed by @andybalaam.
* Add `?no_universal_links=true` to OIDC url so EX doesn't try to handle it ([#29439](https://github.com/element-hq/element-web/pull/29439)). Contributed by @t3chguy.
* Show a blue lock for unencrypted rooms and hide the grey shield for encrypted rooms ([#30440](https://github.com/element-hq/element-web/pull/30440)). Contributed by @langleyd.
* Add support for Module API 1.4 ([#30185](https://github.com/element-hq/element-web/pull/30185)). Contributed by @t3chguy.
* MVVM - Introduce some helpers for snapshot management ([#30398](https://github.com/element-hq/element-web/pull/30398)). Contributed by @MidhunSureshR.
## 🐛 Bug Fixes
* A11y: move focus to right panel when opened ([#30553](https://github.com/element-hq/element-web/pull/30553)). Contributed by @florianduros.
* Fix e2e warning icon should be white ([#30539](https://github.com/element-hq/element-web/pull/30539)). Contributed by @florianduros.
* Remove NoOneHere disabled reason. ([#30524](https://github.com/element-hq/element-web/pull/30524)). Contributed by @toger5.
* Fix downloading files with authenticated media API ([#30520](https://github.com/element-hq/element-web/pull/30520)). Contributed by @t3chguy.
* Fix call permissions check confusion around element call ([#30521](https://github.com/element-hq/element-web/pull/30521)). Contributed by @t3chguy.
* Fix line wrap around emoji verification ([#30523](https://github.com/element-hq/element-web/pull/30523)). Contributed by @t3chguy.
* Don't highlight redacted events ([#30519](https://github.com/element-hq/element-web/pull/30519)). Contributed by @t3chguy.
* Fix matrix.to links not being handled in the app ([#30522](https://github.com/element-hq/element-web/pull/30522)). Contributed by @t3chguy.
* Fix issue of new room list taking up the full width ([#30459](https://github.com/element-hq/element-web/pull/30459)). Contributed by @langleyd.
* Fix widget persistence in React development mode ([#30509](https://github.com/element-hq/element-web/pull/30509)). Contributed by @robintown.
* Fix widget initialization in React development mode ([#30463](https://github.com/element-hq/element-web/pull/30463)). Contributed by @robintown.
Changes in [1.11.109](https://github.com/element-hq/element-desktop/releases/tag/v1.11.109) (2025-08-11)
========================================================================================================
This release supports the upcoming v12 ("hydra") Matrix room version and is necessary to view and participate in these rooms.
## ✨ Features
* [Backport staging] Allow /upgraderoom command without developer mode enabled ([#30529](https://github.com/element-hq/element-web/pull/30529)). Contributed by @RiotRobot.
* [Backport staging] Support for creator/owner power level ([#30526](https://github.com/element-hq/element-web/pull/30526)). Contributed by @RiotRobot.
* New room list: change icon and label of menu item for to start a DM ([#30470](https://github.com/element-hq/element-web/pull/30470)). Contributed by @florianduros.
* Implement the member list with virtuoso ([#29869](https://github.com/element-hq/element-web/pull/29869)). Contributed by @langleyd.
* Add labs option for history sharing on invite ([#30313](https://github.com/element-hq/element-web/pull/30313)). Contributed by @richvdh.
* Bump wysiwyg to 2.39.0 adding support for pasting rich text content in the Rich Text Edtior ([#30421](https://github.com/element-hq/element-web/pull/30421)). Contributed by @langleyd.
* Support `EventShieldReason.MISMATCHED_SENDER` ([#30403](https://github.com/element-hq/element-web/pull/30403)). Contributed by @richvdh.
* Change unencrypted and public pills to blue ([#30399](https://github.com/element-hq/element-web/pull/30399)). Contributed by @florianduros.
* Change color of public room icon ([#30390](https://github.com/element-hq/element-web/pull/30390)). Contributed by @florianduros.
* Script for updating storybook screenshots ([#30340](https://github.com/element-hq/element-web/pull/30340)). Contributed by @dbkr.
* Add toggle to hide empty state in devtools ([#30352](https://github.com/element-hq/element-web/pull/30352)). Contributed by @toger5.
## 🐛 Bug Fixes
* [Backport staging] Use userId to filter users in non-federated rooms when showing the InviteDialog ([#30537](https://github.com/element-hq/element-web/pull/30537)). Contributed by @RiotRobot.
* [Backport staging] Catch error when encountering invalid m.room.pinned\_events event ([#30536](https://github.com/element-hq/element-web/pull/30536)). Contributed by @RiotRobot.
* Update for compatibility with v12 rooms ([#30452](https://github.com/element-hq/element-web/pull/30452)). Contributed by @dbkr.
* New room list: fix tooltip on presence ([#30474](https://github.com/element-hq/element-web/pull/30474)). Contributed by @florianduros.
* New room list: add tooltip for presence and room status ([#30472](https://github.com/element-hq/element-web/pull/30472)). Contributed by @florianduros.
* Fix: Clicking on an item in the member list causes it to scroll to the top rather than show the profile view ([#30455](https://github.com/element-hq/element-web/pull/30455)). Contributed by @langleyd.
* Put the 'decrypting' tooltip back ([#30446](https://github.com/element-hq/element-web/pull/30446)). Contributed by @dbkr.
* Use server name explicitly for via. ([#30362](https://github.com/element-hq/element-web/pull/30362)). Contributed by @Half-Shot.
* fix: replace hardcoded string in poll history dialog ([#30402](https://github.com/element-hq/element-web/pull/30402)). Contributed by @florianduros.
* fix: replace hardcoded string on qr code back button ([#30401](https://github.com/element-hq/element-web/pull/30401)). Contributed by @florianduros.
* Fix color of icon button with outline ([#30361](https://github.com/element-hq/element-web/pull/30361)). Contributed by @florianduros.
Changes in [1.11.108](https://github.com/element-hq/element-desktop/releases/tag/v1.11.108) (2025-07-30)
========================================================================================================
## 🐛 Bug Fixes
* [Backport staging] Fix downloaded attachments not being decrypted ([#30434](https://github.com/element-hq/element-web/pull/30434)). Contributed by @RiotRobot.
Changes in [1.11.107](https://github.com/element-hq/element-desktop/releases/tag/v1.11.107) (2025-07-29)
========================================================================================================
## ✨ Features
* Add support for overlaying notification badges on the Windows Taskbar icon. ([#2443](https://github.com/element-hq/element-desktop/pull/2443)). Contributed by @Half-Shot.
* Reduce macos titlebar height with the new room list and expand the existing border ([#2446](https://github.com/element-hq/element-desktop/pull/2446)). Contributed by @florianduros.
* Message preview should show tooltip with the full message on hover ([#30265](https://github.com/element-hq/element-web/pull/30265)). Contributed by @MidhunSureshR.
* Support rendering notification badges on platforms that do their own icon overlays ([#30315](https://github.com/element-hq/element-web/pull/30315)). Contributed by @Half-Shot.
* Add SubscriptionViewModel base class ([#30297](https://github.com/element-hq/element-web/pull/30297)). Contributed by @dbkr.
* Enhancement: Save image on CTRL+S ([#30330](https://github.com/element-hq/element-web/pull/30330)). Contributed by @ioalexander.
* Add quote functionality to MessageContextMenu (#29893) ([#30323](https://github.com/element-hq/element-web/pull/30323)). Contributed by @AlirezaMrtz.
* Initial structure for shared component views ([#30216](https://github.com/element-hq/element-web/pull/30216)). Contributed by @dbkr.
## 🐛 Bug Fixes
* Reduce macos titlebar height with the new room list and expand the existing border ([#2446](https://github.com/element-hq/element-desktop/pull/2446)). Contributed by @florianduros.
* [Backport staging] Fix e2e shield being invisible in white mode for encrypted room ([#30411](https://github.com/element-hq/element-web/pull/30411)). Contributed by @RiotRobot.
* Force ED titlebar color for new room list ([#30332](https://github.com/element-hq/element-web/pull/30332)). Contributed by @florianduros.
* Add a background color to left panel for macos titlebar in element desktop ([#30328](https://github.com/element-hq/element-web/pull/30328)). Contributed by @florianduros.
* Fix: Prevent page refresh on Enter key in right panel member search ([#30312](https://github.com/element-hq/element-web/pull/30312)). Contributed by @AlirezaMrtz.
Changes in [1.11.106](https://github.com/element-hq/element-desktop/releases/tag/v1.11.106) (2025-07-15)
========================================================================================================
## ✨ Features
* [Backport staging] Fix e2e icon colour ([#30304](https://github.com/element-hq/element-web/pull/30304)). Contributed by @RiotRobot.
* Add support for module message hint `allowDownloadingMedia` ([#30252](https://github.com/element-hq/element-web/pull/30252)). Contributed by @Half-Shot.
* Update the mobile\_guide page to the new design and link out to Element X by default. ([#30172](https://github.com/element-hq/element-web/pull/30172)). Contributed by @pixlwave.
* Filter settings exported when rageshaking ([#30236](https://github.com/element-hq/element-web/pull/30236)). Contributed by @Half-Shot.
* Allow Element Call to learn the room name ([#30213](https://github.com/element-hq/element-web/pull/30213)). Contributed by @robintown.
## 🐛 Bug Fixes
* [Backport staging] Fix missing image download button ([#30322](https://github.com/element-hq/element-web/pull/30322)). Contributed by @RiotRobot.
* Fix transparent verification checkmark in dark mode ([#30235](https://github.com/element-hq/element-web/pull/30235)). Contributed by @Banbuii.
* Fix logic in DeviceListener ([#30230](https://github.com/element-hq/element-web/pull/30230)). Contributed by @uhoreg.
* Disable file drag-and-drop if insufficient permissions ([#30186](https://github.com/element-hq/element-web/pull/30186)). Contributed by @t3chguy.
Changes in [1.11.105](https://github.com/element-hq/element-desktop/releases/tag/v1.11.105) (2025-07-01)
========================================================================================================
## ✨ Features
* Add support for migrating to kwallet6 ([#2390](https://github.com/element-hq/element-desktop/pull/2390)). Contributed by @t3chguy.
* New room list: add context menu to room list item ([#29952](https://github.com/element-hq/element-web/pull/29952)). Contributed by @florianduros.
* Support for custom message components via Module API ([#30074](https://github.com/element-hq/element-web/pull/30074)). Contributed by @Half-Shot.
* Prompt users to set up recovery ([#30075](https://github.com/element-hq/element-web/pull/30075)). Contributed by @uhoreg.
* Update `IconButton` colors ([#30124](https://github.com/element-hq/element-web/pull/30124)). Contributed by @florianduros.
* New room list: filter list can be collapsed ([#29992](https://github.com/element-hq/element-web/pull/29992)). Contributed by @florianduros.
* Show `EmptyRoomListView` when low priority filter matches zero rooms ([#30122](https://github.com/element-hq/element-web/pull/30122)). Contributed by @MidhunSureshR.
## 🐛 Bug Fixes
* Fix element-desktop-ssoid profile deeplinking for OIDC ([#2396](https://github.com/element-hq/element-desktop/pull/2396)). Contributed by @t3chguy.
* Add support for migrating to kwallet6 ([#2390](https://github.com/element-hq/element-desktop/pull/2390)). Contributed by @t3chguy.
* Fix untranslatable string "People" in notifications beta ([#30165](https://github.com/element-hq/element-web/pull/30165)). Contributed by @t3chguy.
* Force verification even after logging in via delegate ([#30141](https://github.com/element-hq/element-web/pull/30141)). Contributed by @andybalaam.
* Hide add integrations button based on UIComponent.AddIntegrations ([#30140](https://github.com/element-hq/element-web/pull/30140)). Contributed by @t3chguy.
* Use nav for new room list and label sections ([#30134](https://github.com/element-hq/element-web/pull/30134)). Contributed by @dbkr.
* Spacestore should emit event after rebuilding home space ([#30132](https://github.com/element-hq/element-web/pull/30132)). Contributed by @MidhunSureshR.
* Handle m.room.pinned\_events being invalid ([#30129](https://github.com/element-hq/element-web/pull/30129)). Contributed by @t3chguy.
Changes in [1.11.104](https://github.com/element-hq/element-desktop/releases/tag/v1.11.104) (2025-06-17)
========================================================================================================
## ✨ Features
* Update the mobile\_guide page to the new design. ([#30006](https://github.com/element-hq/element-web/pull/30006)). Contributed by @pixlwave.
* Provide a devtool for manually verifying other devices ([#30094](https://github.com/element-hq/element-web/pull/30094)). Contributed by @andybalaam.
* Implement MSC4155: Invite filtering ([#29603](https://github.com/element-hq/element-web/pull/29603)). Contributed by @Half-Shot.
* Add low priority avatar decoration to room tile ([#30065](https://github.com/element-hq/element-web/pull/30065)). Contributed by @MidhunSureshR.
* Add ability to prevent window content being captured by other apps (Desktop) ([#30098](https://github.com/element-hq/element-web/pull/30098)). Contributed by @t3chguy.
* New room list: move message preview in user settings ([#30023](https://github.com/element-hq/element-web/pull/30023)). Contributed by @florianduros.
* New room list: change room options icon ([#30029](https://github.com/element-hq/element-web/pull/30029)). Contributed by @florianduros.
* RoomListStore: Sort low priority rooms to the bottom of the list ([#30070](https://github.com/element-hq/element-web/pull/30070)). Contributed by @MidhunSureshR.
* Add low priority filter pill to the room list UI ([#30060](https://github.com/element-hq/element-web/pull/30060)). Contributed by @MidhunSureshR.
* New room list: remove color gradient in space panel ([#29721](https://github.com/element-hq/element-web/pull/29721)). Contributed by @florianduros.
* /share?msg=foo endpoint using forward message dialog ([#29874](https://github.com/element-hq/element-web/pull/29874)). Contributed by @ara4n.
## 🐛 Bug Fixes
* Fix restart loop in safeStorage ([#2373](https://github.com/element-hq/element-desktop/pull/2373)). Contributed by @dbkr.
* Do not send empty auth when setting up cross-signing keys ([#29914](https://github.com/element-hq/element-web/pull/29914)). Contributed by @gnieto.
* Settings: flip local video feed by default ([#29501](https://github.com/element-hq/element-web/pull/29501)). Contributed by @jbtrystram.
* AccessSecretStorageDialog: various fixes ([#30093](https://github.com/element-hq/element-web/pull/30093)). Contributed by @richvdh.
* AccessSecretStorageDialog: fix inability to enter recovery key ([#30090](https://github.com/element-hq/element-web/pull/30090)). Contributed by @richvdh.
* Fix failure to upload thumbnail causing image to send as file ([#30086](https://github.com/element-hq/element-web/pull/30086)). Contributed by @t3chguy.
* Low priority menu item should be a toggle ([#30071](https://github.com/element-hq/element-web/pull/30071)). Contributed by @MidhunSureshR.
* Add sanity checks to prevent users from ignoring themselves ([#30079](https://github.com/element-hq/element-web/pull/30079)). Contributed by @MidhunSureshR.
* Fix issue with duplicate images ([#30073](https://github.com/element-hq/element-web/pull/30073)). Contributed by @fatlewis.
* Handle errors returned from Seshat ([#30083](https://github.com/element-hq/element-web/pull/30083)). Contributed by @richvdh.
Changes in [1.11.103](https://github.com/element-hq/element-desktop/releases/tag/v1.11.103) (2025-06-10)
========================================================================================================
## 🐛 Bug Fixes
+ Check the sender of an event matches owner of session, preventing sender spoofing by homeserver owners.
[13c1d20](https://github.com/matrix-org/matrix-rust-sdk/commit/13c1d2048286bbabf5e7bc6b015aafee98f04d55) (High, [GHSA-x958-rvg6-956w](https://github.com/matrix-org/matrix-rust-sdk/security/advisories/GHSA-x958-rvg6-956w)).
Changes in [1.11.102](https://github.com/element-hq/element-desktop/releases/tag/v1.11.102) (2025-06-03)
========================================================================================================
## ✨ Features
* Support build-time specified protocol scheme for oidc callback ([#2285](https://github.com/element-hq/element-desktop/pull/2285)). Contributed by @t3chguy.
* EW: Modernize the recovery key input modal ([#29819](https://github.com/element-hq/element-web/pull/29819)). Contributed by @uhoreg.
* New room list: move secondary filters into primary filters ([#29972](https://github.com/element-hq/element-web/pull/29972)). Contributed by @florianduros.
* Prompt the user when key storage is unexpectedly off ([#29912](https://github.com/element-hq/element-web/pull/29912)). Contributed by @andybalaam.
* New room list: move sort menu in room list header ([#29983](https://github.com/element-hq/element-web/pull/29983)). Contributed by @florianduros.
* New room list: rework spacing of room list item ([#29965](https://github.com/element-hq/element-web/pull/29965)). Contributed by @florianduros.
* RLS: Remove forgotten room from skiplist ([#29933](https://github.com/element-hq/element-web/pull/29933)). Contributed by @MidhunSureshR.
* Add room list sorting ([#29951](https://github.com/element-hq/element-web/pull/29951)). Contributed by @dbkr.
* Don't use the minimised width(68px) on the new room list ([#29778](https://github.com/element-hq/element-web/pull/29778)). Contributed by @langleyd.
## 🐛 Bug Fixes
* Enable plain text encryption before checking if encryption is available ([#2343](https://github.com/element-hq/element-desktop/pull/2343)). Contributed by @MidhunSureshR.
* Enable plain text encryption early if we actually mean to use `basic_text` as backend ([#2341](https://github.com/element-hq/element-desktop/pull/2341)). Contributed by @MidhunSureshR.
* [Backport staging] Close call options popup menu when option has been selected ([#30054](https://github.com/element-hq/element-web/pull/30054)). Contributed by @RiotRobot.
* RoomListStoreV3: Only add new rooms that pass `VisibilityProvider` check ([#29974](https://github.com/element-hq/element-web/pull/29974)). Contributed by @MidhunSureshR.
* Re-order primary filters ([#29957](https://github.com/element-hq/element-web/pull/29957)). Contributed by @dbkr.
* Fix leaky CSS adding `!` to all H1 elements ([#29964](https://github.com/element-hq/element-web/pull/29964)). Contributed by @t3chguy.
* Fix extensions panel style ([#29273](https://github.com/element-hq/element-web/pull/29273)). Contributed by @langleyd.
* Fix state events being hidden from widgets in read\_events actions ([#29954](https://github.com/element-hq/element-web/pull/29954)). Contributed by @robintown.
* Remove old filter test ([#29963](https://github.com/element-hq/element-web/pull/29963)). Contributed by @dbkr.
Changes in [1.11.101](https://github.com/element-hq/element-desktop/releases/tag/v1.11.101) (2025-05-20)
========================================================================================================
## ✨ Features

View File

@@ -52,9 +52,7 @@ If you only want to run the app locally and don't need to build packages, you ca
provide the `webapp` directory directly:
```
# Assuming you've checked out and built a copy of element-web in ../element-web.
# Note that you will not be able to `yarn build` after this, but `yarn start`
# will work fine.
# Assuming you've checked out and built a copy of element-web in ../element-web
ln -s ../element-web/webapp ./
```

BIN
build/icons/128x128.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
build/icons/16x16.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
build/icons/24x24.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
build/icons/256x256.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
build/icons/48x48.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
build/icons/64x64.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
build/icons/96x96.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,6 +1,6 @@
# Docker image to facilitate building Element Desktop's native bits using a glibc version (2.31)
# with broader compatibility, down to Debian bullseye & Ubuntu focal.
FROM rust:bullseye@sha256:4886b155a84b46ad61df6a2249905bea8c61fc1eaa903398713233858ff6306b
FROM rust:bullseye@sha256:f40b8cc3195deda321031e8dfe23c7d2586e3db7c4103fa36946982d9fd6d588
ENV DEBIAN_FRONTEND=noninteractive
@@ -9,6 +9,8 @@ RUN apt-get -qq update && apt-get -y -qq dist-upgrade && \
apt-get -y -qq install --no-install-recommends \
# tclsh is required for building SQLite as part of SQLCipher
tcl \
# libsecret-1-dev is required even for prebuild keytar
libsecret-1-dev \
# Used by seshat (when not SQLCIPHER_STATIC) \
libsqlcipher-dev && \
apt-get purge -y --auto-remove && rm -rf /var/lib/apt/lists/*

View File

@@ -3,9 +3,6 @@
set -x
declare -A archMap=(["amd64"]="x64" ["arm64"]="arm64")
ARCH="${archMap["$TARGETARCH"]}"
# The .node-version file generally doesn't have the 'v' (renovate does not put the 'v' and will
# strip it on upgrade if it's there) but the 'v' is also widely supported so we probably ought
# to just work either way.
NODE_VERSION=$(cat /.node-version | sed -e 's/^v//')
curl --proto "=https" -L "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-$TARGETOS-$ARCH.tar.gz" | tar xz -C /usr/local --strip-components=1 && \
NODE_VERSION=$(cat /.node-version)
curl --proto "=https" -L "https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-$TARGETOS-$ARCH.tar.gz" | tar xz -C /usr/local --strip-components=1 && \
unlink /usr/local/CHANGELOG.md && unlink /usr/local/LICENSE && unlink /usr/local/README.md

View File

@@ -13,35 +13,12 @@ custom build of Element without installing the various build tools required.
The process is automated by [vector-im/element-builder](https://github.com/vector-im/element-builder)
when releasing.
## Use docker
If you are building for Linux, you can build the native modules with:
```
yarn docker:setup
yarn docker:install
INDOCKER_SQLCIPHER_BUNDLED=1 yarn docker:build:native
```
The above will build `matrix-seshat` in
`docker/node_modules/matrix-seshat`. You can then either run `yarn docker:build`
to build the app inside docker, or:
```
yarn --cwd docker/node_modules/matrix-seshat link
yarn link matrix-seshat
```
... and build the app with `yarn build` or run it with `yarn start`.
(See also https://github.com/element-hq/element-desktop#docker.)
## Building
Install the pre-requisites for your system:
- [Windows pre-requisites](https://github.com/vector-im/element-desktop/blob/develop/docs/windows-requirements.md)
- Linux: TODO. Using the docker environment as above is recommended.
- Linux: TODO
- OS X: TODO
Then optionally, [add seshat and dependencies to support search in E2E rooms](#adding-seshat-for-search-in-e2e-encrypted-rooms).
@@ -79,8 +56,7 @@ This is also needed to when pulling in changes to Seshat using `yarn link`.
Recompiling Seshat itself can be done like so:
ELECTRON_VERSION=$(electron --version)
yarn run electron-build-env -- --electron ${ELECTRON_VERSION#v} -- neon build matrix-seshat --release
yarn run electron-build-env -- --electron 6.1.1 -- neon build matrix-seshat --release
Please make sure to include all the `--` as well as the `--release` command line
switch at the end. Modify your electron version accordingly depending on the

View File

@@ -1,12 +1,12 @@
import * as os from "node:os";
import * as fs from "node:fs";
import * as path from "node:path";
import { type Configuration as BaseConfiguration, type Protocol } from "electron-builder";
/**
* This script has different outputs depending on your os platform.
*
* On Windows:
* Prefixes the nightly version with `0.0.1-nightly.` as it breaks if it is not semver
* Passes $ED_SIGNTOOL_THUMBPRINT and $ED_SIGNTOOL_SUBJECT_NAME to
* build.win.signtoolOptions.signingHashAlgorithms and build.win.signtoolOptions.certificateSubjectName respectively if specified.
*
@@ -16,68 +16,32 @@ import { type Configuration as BaseConfiguration, type Protocol } from "electron
* Passes $ED_DEBIAN_CHANGELOG to build.deb.fpm if specified
*/
/**
* Interface describing relevant fields of the package.json file.
*/
interface Pkg {
version: string;
}
const DEFAULT_APP_ID = "im.riot.app";
const NIGHTLY_APP_ID = "im.riot.nightly";
const NIGHTLY_DEB_NAME = "element-nightly";
/**
* Base metadata fields, used in both package.json and the variant configuration.
*/
interface Metadata {
const DEFAULT_PROTOCOL_SCHEME = "io.element.desktop";
const NIGHTLY_PROTOCOL_SCHEME = "io.element.nightly";
interface Pkg {
name: string;
productName: string;
description: string;
}
/**
* Extra metadata fields that are injected into the build to pass to the app at runtime.
*/
interface ExtraMetadata extends Metadata {
electron_appId: string;
electron_protocol: string;
}
/**
* Interface describing the variant configuration format.
*/
interface Variant extends Metadata {
"appId": string;
"linux.executableName"?: string;
"linux.deb.name"?: string;
"protocols": string[];
version: string;
}
type Writable<T> = NonNullable<
T extends Function ? T : T extends object ? { -readonly [K in keyof T]: Writable<T[K]> } : T
>;
// Load the default variant as a base configuration
const DEFAULT_VARIANT = path.join("element.io", "release", "build.json");
let variant: Variant = JSON.parse(fs.readFileSync(DEFAULT_VARIANT, "utf8"));
/**
* If a variant is specified, we will use it to override the build-specific values.
* This allows us to have different builds for different purposes (e.g. stable, nightly).
*/
if (process.env.VARIANT_PATH) {
console.log(`Using variant configuration from '${process.env.VARIANT_PATH}':`);
variant = {
...variant,
...JSON.parse(fs.readFileSync(`${process.env.VARIANT_PATH}`, "utf8")),
};
} else {
console.warn(`No VARIANT_PATH specified, using default variant configuration '${DEFAULT_VARIANT}':`);
}
for (const key in variant) {
console.log(`${key}: ${variant[key]}`);
}
const pkg: Pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
interface Configuration extends BaseConfiguration {
extraMetadata: Partial<Pick<Pkg, "version">> & ExtraMetadata;
extraMetadata: Partial<Pick<Pkg, "version">> &
Omit<Pkg, "version"> & {
electron_appId: string;
electron_protocol: string;
};
linux: BaseConfiguration["linux"];
win: BaseConfiguration["win"];
mac: BaseConfiguration["mac"];
@@ -94,7 +58,7 @@ const config: Omit<Writable<Configuration>, "electronFuses"> & {
// Make all fuses required to ensure they are all explicitly specified
electronFuses: Required<Configuration["electronFuses"]>;
} = {
appId: variant.appId,
appId: DEFAULT_APP_ID,
asarUnpack: "**/*.node",
electronFuses: {
enableCookieEncryption: true,
@@ -118,19 +82,25 @@ const config: Omit<Writable<Configuration>, "electronFuses"> & {
},
"lib/**",
],
extraResources: ["build/icon.*", "webapp.asar"],
extraResources: [
{
from: "res/img",
to: "img",
},
"webapp.asar",
],
extraMetadata: {
name: variant.name,
productName: variant.productName,
description: variant.description,
electron_appId: variant.appId,
electron_protocol: variant.protocols[0],
name: pkg.name,
productName: pkg.productName,
description: pkg.description,
electron_appId: DEFAULT_APP_ID,
electron_protocol: DEFAULT_PROTOCOL_SCHEME,
},
linux: {
target: ["tar.gz", "deb"],
category: "Network;InstantMessaging;Chat",
icon: "icon.png",
executableName: variant.name, // element-desktop or element-desktop-nightly
maintainer: "support@element.io",
icon: "build/icons",
},
deb: {
packageCategory: "net",
@@ -148,17 +118,24 @@ const config: Omit<Writable<Configuration>, "electronFuses"> & {
"libgbm1",
],
recommends: ["libsqlcipher0", "element-io-archive-keyring"],
fpm: ["--deb-pre-depends", "libc6 (>= 2.31)"],
fpm: [
"--deb-field",
"Replaces: riot-desktop (<< 1.7.0), riot-web (<< 1.7.0)",
"--deb-field",
"Breaks: riot-desktop (<< 1.7.0), riot-web (<< 1.7.0)",
"--deb-pre-depends",
"libc6 (>= 2.31)",
],
},
mac: {
target: ["dmg", "zip"],
category: "public.app-category.social-networking",
darkModeSupport: true,
hardenedRuntime: true,
gatekeeperAssess: true,
strictVerify: true,
// XXX: we cannot specify this due to https://github.com/electron/osx-sign/issues/344
// strictVerify: true,
entitlements: "./build/entitlements.mac.plist",
icon: "build/icon.icns",
icon: "build/icons/icon.icns",
mergeASARs: true,
x64ArchFiles: "**/matrix-seshat/*.node", // hak already runs lipo
},
@@ -167,7 +144,7 @@ const config: Omit<Writable<Configuration>, "electronFuses"> & {
signtoolOptions: {
signingHashAlgorithms: ["sha256"],
},
icon: "build/icon.ico",
icon: "build/icons/icon.ico",
},
msi: {
perMachine: true,
@@ -176,27 +153,14 @@ const config: Omit<Writable<Configuration>, "electronFuses"> & {
output: "dist",
},
protocols: {
name: variant.productName,
schemes: variant.protocols,
name: "element",
schemes: [DEFAULT_PROTOCOL_SCHEME, "element"],
},
nativeRebuilder: "sequential",
nodeGypRebuild: false,
npmRebuild: true,
};
/**
* Allow specifying the version via env var.
* If unspecified, it will default to the version in package.json.
* @param {string} process.env.VERSION
*/
if (process.env.VERSION) {
config.extraMetadata.version = process.env.VERSION;
}
if (variant["linux.deb.name"]) {
config.deb.fpm.push("--name", variant["linux.deb.name"]);
}
/**
* Allow specifying windows signing cert via env vars
* @param {string} process.env.ED_SIGNTOOL_SUBJECT_NAME
@@ -207,6 +171,32 @@ if (process.env.ED_SIGNTOOL_SUBJECT_NAME && process.env.ED_SIGNTOOL_THUMBPRINT)
config.win.signtoolOptions!.certificateSha1 = process.env.ED_SIGNTOOL_THUMBPRINT;
}
/**
* Allow specifying nightly version via env var
* @param {string} process.env.ED_NIGHTLY
*/
if (process.env.ED_NIGHTLY) {
config.deb.fpm = []; // Clear the fpm as the breaks deb fields don't apply to nightly
config.appId = config.extraMetadata.electron_appId = NIGHTLY_APP_ID;
config.extraMetadata.productName += " Nightly";
config.extraMetadata.name += "-nightly";
config.extraMetadata.description += " (nightly unstable build)";
config.deb.fpm.push("--name", NIGHTLY_DEB_NAME);
(config.protocols as Protocol).schemes[0] = config.extraMetadata.electron_protocol = NIGHTLY_PROTOCOL_SCHEME;
let version = process.env.ED_NIGHTLY;
if (os.platform() === "win32") {
// The windows packager relies on parsing this as semver, so we have to make it look like one.
// This will give our update packages really stupid names, but we probably can't change that either
// because squirrel windows parses them for the version too. We don't really care: nobody sees them.
// We just give the installer a static name, so you'll just see this in the 'about' dialog.
// Turns out if you use 0.0.0 here it makes Squirrel windows crash, so we use 0.0.1.
version = "0.0.1-nightly." + version;
}
config.extraMetadata.version = version;
}
if (os.platform() === "linux") {
// Electron crashes on debian if there's a space in the path.
// https://github.com/vector-im/element-web/issues/13171

View File

@@ -1,9 +0,0 @@
{
"appId": "im.riot.nightly",
"name": "element-desktop-nightly",
"productName": "Element Nightly",
"description": "Element: the future of secure communication (nightly unstable build)",
"protocols": ["io.element.nightly", "element"],
"linux.executableName": "element-desktop-nightly",
"linux.deb.name": "element-nightly"
}

View File

@@ -19,7 +19,7 @@
"https://scalar-staging.vector.im/api",
"https://scalar-staging.riot.im/scalar/api"
],
"bug_report_endpoint_url": "https://rageshakes.element.io/api/submit",
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
"uisi_autorageshake_app": "element-auto-uisi",
"show_labs_settings": true,
"room_directory": {

View File

@@ -1,7 +0,0 @@
{
"appId": "im.riot.app",
"name": "element-desktop",
"productName": "Element",
"description": "Element: the future of secure communication",
"protocols": ["io.element.desktop", "element"]
}

View File

@@ -19,7 +19,7 @@
"https://scalar-staging.vector.im/api",
"https://scalar-staging.riot.im/scalar/api"
],
"bug_report_endpoint_url": "https://rageshakes.element.io/api/submit",
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
"uisi_autorageshake_app": "element-auto-uisi",
"room_directory": {
"servers": ["matrix.org", "gitter.im"]

View File

@@ -1,7 +1,7 @@
import { KnipConfig } from "knip";
export default {
entry: ["src/preload.cts", "electron-builder.ts", "scripts/**", "hak/**"],
entry: ["src/electron-main.ts", "src/preload.ts", "electron-builder.ts", ".eslintrc-*.js", "scripts/**", "hak/**"],
project: ["**/*.{js,ts}"],
ignoreDependencies: [
// Brought in via hak scripts
@@ -10,8 +10,6 @@ export default {
"@action-validator/*",
// Used for git pre-commit hooks
"husky",
// Required for `patch-package`
"postinstall-postinstall",
],
ignoreBinaries: ["jq", "scripts/in-docker.sh"],
} satisfies KnipConfig;

View File

@@ -3,12 +3,9 @@
"productName": "Element",
"main": "lib/electron-main.js",
"exports": "./lib/electron-main.js",
"version": "1.12.7",
"version": "1.11.101",
"description": "Element: the future of secure communication",
"author": {
"name": "Element",
"email": "support@element.io"
},
"author": "Element",
"homepage": "https://element.io",
"repository": {
"type": "git",
@@ -56,18 +53,20 @@
"test": "playwright test",
"test:open": "yarn test --ui",
"test:screenshots:build": "docker build playwright -t element-desktop-playwright --platform linux/amd64",
"test:screenshots:run": "docker run --rm --network host -v $(pwd):/work/element-desktop -v element-desktop-playwright:/work/element-desktop/node_modules -v /var/run/docker.sock:/var/run/docker.sock --platform linux/amd64 -it element-desktop-playwright",
"postinstall": "patch-package && electron-builder install-app-deps"
"test:screenshots:run": "docker run --rm --network host -v $(pwd):/work/element-desktop -v /var/run/docker.sock:/var/run/docker.sock --platform linux/amd64 -it element-desktop-playwright",
"postinstall": "electron-builder install-app-deps"
},
"dependencies": {
"@sentry/electron": "^7.0.0",
"@sentry/electron": "^6.0.0",
"@standardnotes/electron-clear-data": "^1.0.5",
"auto-launch": "^5.0.5",
"counterpart": "^0.18.6",
"electron-store": "^11.0.0",
"electron-store": "^10.0.0",
"electron-window-state": "^5.0.3",
"keytar-forked": "7.10.0",
"minimist": "^1.2.6",
"png-to-ico": "^3.0.0",
"uuid": "^13.0.0"
"png-to-ico": "^2.1.1",
"uuid": "^11.0.0"
},
"devDependencies": {
"@action-validator/cli": "^0.6.0",
@@ -75,51 +74,49 @@
"@babel/core": "^7.18.10",
"@babel/preset-env": "^7.18.10",
"@babel/preset-typescript": "^7.18.6",
"@electron/asar": "4.0.1",
"@playwright/test": "1.57.0",
"@stylistic/eslint-plugin": "^5.0.0",
"@electron/asar": "3.4.1",
"@playwright/test": "1.52.0",
"@stylistic/eslint-plugin": "^4.0.0",
"@types/auto-launch": "^5.0.1",
"@types/counterpart": "^0.18.1",
"@types/minimist": "^1.2.1",
"@types/node": "18.19.130",
"@types/node": "18.19.100",
"@types/pacote": "^11.1.1",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"app-builder-lib": "26.2.0",
"chokidar": "^5.0.0",
"app-builder-lib": "26.0.15",
"chokidar": "^4.0.0",
"detect-libc": "^2.0.0",
"electron": "39.2.3",
"electron-builder": "26.2.0",
"electron-builder-squirrel-windows": "26.2.0",
"electron": "36.2.0",
"electron-builder": "26.0.15",
"electron-builder-squirrel-windows": "26.0.15",
"electron-devtools-installer": "^4.0.0",
"eslint": "^8.26.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^10.0.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-matrix-org": "^3.0.0",
"eslint-plugin-matrix-org": "^2.0.1",
"eslint-plugin-n": "^17.12.0",
"eslint-plugin-unicorn": "^56.0.0",
"glob": "^13.0.0",
"glob": "^11.0.0",
"husky": "^9.1.6",
"knip": "^5.0.0",
"lint-staged": "^16.0.0",
"matrix-web-i18n": "^3.2.1",
"mkdirp": "^3.0.0",
"pacote": "^21.0.0",
"patch-package": "^8.0.1",
"postinstall-postinstall": "^2.1.0",
"prettier": "^3.0.0",
"rimraf": "^6.0.0",
"tar": "^7.0.0",
"tsx": "^4.19.2",
"typescript": "5.9.3"
"typescript": "5.8.3"
},
"hakDependencies": {
"matrix-seshat": "^4.0.1"
},
"resolutions": {
"@types/node": "18.19.130",
"@types/node": "18.19.100",
"config-file-ts": "0.2.8-rc1",
"node-abi": "4.24.0"
"node-abi": "4.8.0"
}
}

View File

@@ -1,18 +0,0 @@
diff --git a/node_modules/@types/auto-launch/index.d.ts b/node_modules/@types/auto-launch/index.d.ts
index a30a77c..e512ce1 100644
--- a/node_modules/@types/auto-launch/index.d.ts
+++ b/node_modules/@types/auto-launch/index.d.ts
@@ -25,6 +25,13 @@ interface AutoLaunchOptions {
declare class AutoLaunch {
constructor(options: AutoLaunchOptions);
+ /**
+ * This type describes the internal options of the `auto-launch` package which allows us to update options after initialization.
+ */
+ readonly opts: {
+ isHiddenOnLaunch: boolean;
+ };
+
/**
* Enables auto-launch at start up.
*/

View File

@@ -1,11 +1,8 @@
FROM mcr.microsoft.com/playwright:v1.57.0-jammy@sha256:6aca677c27a967caf7673d108ac67ffaf8fed134f27e17b27a05464ca0ace831
FROM mcr.microsoft.com/playwright:v1.52.0-jammy@sha256:ff2946177f0756c87482c0ef958b7cfbf389b92525ace78a1c9890281d0d60f4
WORKDIR /work/element-desktop
RUN apt-get update && apt-get -y install xvfb dbus-x11 && apt-get purge -y --auto-remove && rm -rf /var/lib/apt/lists/*
# Create node_modules & dist dirs so that the volumes have the correct permissions
RUN mkdir node_modules dist && chown 1000:1000 node_modules dist
RUN apt-get update && apt-get -y install xvfb && apt-get purge -y --auto-remove && rm -rf /var/lib/apt/lists/*
USER 1000:1000

View File

@@ -8,11 +8,4 @@ sleep 2
export DISPLAY=:99
yarn install --frozen-lockfile
yarn build -l --dir
PLAYWRIGHT_HTML_OPEN=never ELEMENT_DESKTOP_EXECUTABLE="./dist/linux-unpacked/element-desktop" \
npx playwright test --update-snapshots --reporter line,html "$1"
# Clean up
rm -R core qemu_* || exit 0
npx playwright test --update-snapshots --reporter line $1

View File

@@ -6,6 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import keytar from "keytar-forked";
import { test, expect } from "../../element-desktop-test.js";
declare global {
@@ -59,6 +61,34 @@ test.describe("App launch", () => {
),
).resolves.not.toBeNull();
});
test.describe("migrate from keytar", () => {
test.skip(
process.env.GITHUB_ACTIONS && ["linux", "darwin"].includes(process.platform),
"GitHub Actions hosted runner are not a compatible environment for this test",
);
const pickleKey = "DEADBEEF1234";
const keytarService = "element.io";
const keytarKey = `${userId}|${deviceId}`;
test.beforeAll(async () => {
await keytar.setPassword(keytarService, keytarKey, pickleKey);
await expect(keytar.getPassword(keytarService, keytarKey)).resolves.toBe(pickleKey);
});
test.afterAll(async () => {
await keytar.deletePassword(keytarService, keytarKey);
});
test("should migrate successfully", async ({ page }) => {
await expect(
page.evaluate(
([userId, deviceId]) => window.mxPlatformPeg.get().getPickleKey(userId, deviceId),
[userId, deviceId],
),
).resolves.toBe(pickleKey);
});
});
});
test.describe("--no-update", () => {

View File

@@ -84,7 +84,7 @@ export const test = base.extend<Fixtures>({
args.unshift(path.join(__dirname, "..", "lib", "electron-main.js"));
}
console.log(`Launching '${executablePath || "electron"}' with args ${args.join(" ")}`);
console.log(`Launching '${executablePath}' with args ${args.join(" ")}`);
const app = await electron.launch({
env: {

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
res/img/element.ico Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
res/img/element.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -7,17 +7,41 @@ Please see LICENSE files in the repository root for full details.
*/
import path from "node:path";
import os from "node:os";
import fsProm from "node:fs/promises";
import type HakEnv from "./hakEnv.js";
import { type DependencyInfo } from "./dep.js";
export default async function link(hakEnv: HakEnv, moduleInfo: DependencyInfo): Promise<void> {
const linkFolder = path.join(hakEnv.dotHakDir, "links");
await hakEnv.spawn("yarn", ["link", "--link-folder", linkFolder], {
const yarnrc = path.join(hakEnv.projectRoot, ".yarnrc");
// this is fairly terrible but it's reasonably clunky to either parse a yarnrc
// properly or get yarn to do it, so this will probably suffice for now.
// We just check to see if there is a local .yarnrc at all, and assume that
// if you've put one there yourself, you probably know what you're doing and
// we won't meddle with it.
// Also we do this for each module which is unnecessary, but meh.
try {
await fsProm.stat(yarnrc);
} catch {
await fsProm.writeFile(
yarnrc,
// XXX: 1. This must be absolute, as yarn will resolve link directories
// relative to the closest project root, which means when we run it
// in the dependency project, it will put the link directory in its
// own project folder rather than the main project.
// 2. The parser gets very confused by strings with colons in them
// (ie. Windows absolute paths) but strings in quotes get parsed as
// JSON so need to be valid JSON encoded strings (ie. have the
// backslashes escaped). JSON.stringify will add quotes and escape.
"--link-folder " + JSON.stringify(path.join(hakEnv.dotHakDir, "links")) + os.EOL,
);
}
await hakEnv.spawn("yarn", ["link"], {
cwd: moduleInfo.moduleOutDir,
});
await hakEnv.spawn("yarn", ["link", "--link-folder", linkFolder, moduleInfo.name], {
await hakEnv.spawn("yarn", ["link", moduleInfo.name], {
cwd: hakEnv.projectRoot,
});
}

View File

@@ -1,9 +1,5 @@
#!/bin/bash
set -e
cd $(dirname "$0")/..
IMAGE=${DOCKER_IMAGE_NAME:-"element-desktop-dockerbuild"}
docker inspect "$IMAGE" 2> /dev/null > /dev/null
@@ -12,8 +8,6 @@ if [ $? != 0 ]; then
exit 1
fi
mkdir -p docker/node_modules docker/.hak docker/.gnupg
# Taken from https://www.electron.build/multi-platform-build#docker
# Pass through any vars prefixed with INDOCKER_, removing the prefix
docker run --rm -ti \

View File

@@ -7,6 +7,7 @@ Please see LICENSE files in the repository root for full details.
import { type BrowserWindow } from "electron";
import type AutoLaunch from "auto-launch";
import { type AppLocalization } from "../language-helper.js";
// global type extensions need to use var for whatever reason
@@ -17,6 +18,7 @@ declare global {
var mainWindow: BrowserWindow | null;
var appQuitting: boolean;
var appLocalization: AppLocalization;
var launcher: AutoLaunch;
var vectorConfig: IConfigOptions;
var trayConfig: {
// eslint-disable-next-line camelcase

View File

@@ -1,50 +0,0 @@
/*
Copyright 2025 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import BaseAutoLaunch from "auto-launch";
import Store from "./store.js";
export type AutoLaunchState = "enabled" | "minimised" | "disabled";
// Wrapper around auto-launch to get/set the `isHidden` option
export class AutoLaunch extends BaseAutoLaunch {
private static internalInstance?: AutoLaunch;
public static get instance(): AutoLaunch {
if (!AutoLaunch.internalInstance) {
if (!Store.instance) throw new Error("Store not initialized");
AutoLaunch.internalInstance = new AutoLaunch({
name: global.vectorConfig.brand || "Element",
isHidden: Store.instance.get("openAtLoginMinimised"),
mac: {
useLaunchAgent: true,
},
});
}
return AutoLaunch.internalInstance;
}
public async getState(): Promise<AutoLaunchState> {
if (!(await this.isEnabled())) {
return "disabled";
}
return this.opts.isHiddenOnLaunch ? "minimised" : "enabled";
}
public async setState(state: AutoLaunchState): Promise<void> {
const openAtLoginMinimised = state === "minimised";
Store.instance?.set("openAtLoginMinimised", openAtLoginMinimised);
this.opts.isHiddenOnLaunch = openAtLoginMinimised;
if (state !== "disabled") {
return this.enable();
} else {
return this.disable();
}
}
}

View File

@@ -1,49 +0,0 @@
/*
Copyright 2025 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { app, ipcMain, type IpcMainEvent, nativeImage } from "electron";
import { _t } from "./language-helper.js";
// Handles calculating the correct "badge" for the window, for notifications and error states.
// Tray icon updates are handled in tray.ts
if (process.platform === "win32") {
// We only use setOverlayIcon on Windows as it's only supported on that platform, but has good support
// from all the Windows variants we support.
// https://www.electronjs.org/docs/latest/api/browser-window#winsetoverlayiconoverlay-description-windows
ipcMain.on(
"setBadgeCount",
function (_ev: IpcMainEvent, count: number, imageBuffer?: Buffer, isError?: boolean): void {
if (count === 0) {
// Flash frame is set to true in ipc.ts "loudNotification"
global.mainWindow?.flashFrame(false);
}
if (imageBuffer) {
global.mainWindow?.setOverlayIcon(
nativeImage.createFromBuffer(Buffer.from(imageBuffer)),
isError
? _t("icon_overlay|description_error")
: _t("icon_overlay|description_notifications", { count }),
);
} else {
global.mainWindow?.setOverlayIcon(null, "");
}
},
);
} else {
// only set badgeCount on Mac/Linux, the docs say that only those platforms support it but turns out Electron
// has some Windows support too, and in some Windows environments this leads to two badges rendering atop
// each other. See https://github.com/vector-im/element-web/issues/16942
ipcMain.on("setBadgeCount", function (_ev: IpcMainEvent, count: number): void {
if (count === 0) {
// Flash frame is set to true in ipc.ts "loudNotification"
global.mainWindow?.flashFrame(false);
}
app.badgeCount = count;
});
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2018-2025 New Vector Ltd.
Copyright 2018-2024 New Vector Ltd.
Copyright 2017-2019 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2016 Aviral Dasgupta
Copyright 2016 OpenMarket Ltd
@@ -10,20 +10,10 @@ Please see LICENSE files in the repository root for full details.
// Squirrel on windows starts the app with various flags as hooks to tell us when we've been installed/uninstalled etc.
import "./squirrelhooks.js";
import {
app,
BrowserWindow,
Menu,
autoUpdater,
dialog,
type Input,
type Event,
session,
protocol,
desktopCapturer,
} from "electron";
import { app, BrowserWindow, Menu, autoUpdater, dialog, type Input, type Event, session, protocol } from "electron";
// eslint-disable-next-line n/file-extension-in-import
import * as Sentry from "@sentry/electron/main";
import AutoLaunch from "auto-launch";
import path, { dirname } from "node:path";
import windowStateKeeper from "electron-window-state";
import fs, { promises as afs } from "node:fs";
@@ -33,7 +23,6 @@ import minimist from "minimist";
import "./ipc.js";
import "./seshat.js";
import "./settings.js";
import "./badge.js";
import * as tray from "./tray.js";
import Store from "./store.js";
import { buildMenuTemplate } from "./vectormenu.js";
@@ -168,75 +157,59 @@ function loadLocalConfigFile(): Json {
}
}
let loadConfigPromise: Promise<void> | undefined;
// Loads the config from asar, and applies a config.json from userData atop if one exists
// Writes config to `global.vectorConfig`. Idempotent, returns the same promise on subsequent calls.
function loadConfig(): Promise<void> {
if (loadConfigPromise) return loadConfigPromise;
// Writes config to `global.vectorConfig`. Does nothing if `global.vectorConfig` is already set.
async function loadConfig(): Promise<void> {
if (global.vectorConfig) return;
async function actuallyLoadConfig(): Promise<void> {
const asarPath = await getAsarPath();
const asarPath = await getAsarPath();
try {
console.log(`Loading app config: ${path.join(asarPath, LocalConfigFilename)}`);
global.vectorConfig = loadJsonFile(asarPath, LocalConfigFilename);
} catch {
// it would be nice to check the error code here and bail if the config
// is unparsable, but we get MODULE_NOT_FOUND in the case of a missing
// file or invalid json, so node is just very unhelpful.
// Continue with the defaults (ie. an empty config)
global.vectorConfig = {};
try {
console.log(`Loading app config: ${path.join(asarPath, LocalConfigFilename)}`);
global.vectorConfig = loadJsonFile(asarPath, LocalConfigFilename);
} catch {
// it would be nice to check the error code here and bail if the config
// is unparsable, but we get MODULE_NOT_FOUND in the case of a missing
// file or invalid json, so node is just very unhelpful.
// Continue with the defaults (ie. an empty config)
global.vectorConfig = {};
}
try {
// Load local config and use it to override values from the one baked with the build
const localConfig = loadLocalConfigFile();
// If the local config has a homeserver defined, don't use the homeserver from the build
// config. This is to avoid a problem where Riot thinks there are multiple homeservers
// defined, and panics as a result.
if (Object.keys(localConfig).find((k) => homeserverProps.includes(<any>k))) {
// Rip out all the homeserver options from the vector config
global.vectorConfig = Object.keys(global.vectorConfig)
.filter((k) => !homeserverProps.includes(<any>k))
.reduce(
(obj, key) => {
obj[key] = global.vectorConfig[key];
return obj;
},
{} as Omit<Partial<(typeof global)["vectorConfig"]>, keyof typeof homeserverProps>,
);
}
try {
// Load local config and use it to override values from the one baked with the build
const localConfig = loadLocalConfigFile();
// If the local config has a homeserver defined, don't use the homeserver from the build
// config. This is to avoid a problem where Riot thinks there are multiple homeservers
// defined, and panics as a result.
if (Object.keys(localConfig).find((k) => homeserverProps.includes(<any>k))) {
// Rip out all the homeserver options from the vector config
global.vectorConfig = Object.keys(global.vectorConfig)
.filter((k) => !homeserverProps.includes(<any>k))
.reduce(
(obj, key) => {
obj[key] = global.vectorConfig[key];
return obj;
},
{} as Omit<Partial<(typeof global)["vectorConfig"]>, keyof typeof homeserverProps>,
);
}
global.vectorConfig = Object.assign(global.vectorConfig, localConfig);
} catch (e) {
if (e instanceof SyntaxError) {
await app.whenReady();
void dialog.showMessageBox({
type: "error",
title: `Your ${global.vectorConfig.brand || "Element"} is misconfigured`,
message:
`Your custom ${global.vectorConfig.brand || "Element"} configuration contains invalid JSON. ` +
`Please correct the problem and reopen ${global.vectorConfig.brand || "Element"}.`,
detail: e.message || "",
});
}
// Could not load local config, this is expected in most cases.
}
// Tweak modules paths as they assume the root is at the same level as webapp, but for `vector://vector/webapp` it is not.
if (Array.isArray(global.vectorConfig.modules)) {
global.vectorConfig.modules = global.vectorConfig.modules.map((m) => {
if (m.startsWith("/")) {
return "/webapp" + m;
}
return m;
global.vectorConfig = Object.assign(global.vectorConfig, localConfig);
} catch (e) {
if (e instanceof SyntaxError) {
void dialog.showMessageBox({
type: "error",
title: `Your ${global.vectorConfig.brand || "Element"} is misconfigured`,
message:
`Your custom ${global.vectorConfig.brand || "Element"} configuration contains invalid JSON. ` +
`Please correct the problem and reopen ${global.vectorConfig.brand || "Element"}.`,
detail: e.message || "",
});
}
// Could not load local config, this is expected in most cases.
}
loadConfigPromise = actuallyLoadConfig();
return loadConfigPromise;
}
// Configure Electron Sentry and crashReporter using sentry.dsn in config.json if one is present.
@@ -254,17 +227,54 @@ async function configureSentry(): Promise<void> {
}
}
// Set up globals for Tray
// Set up globals for Tray and AutoLaunch
async function setupGlobals(): Promise<void> {
const asarPath = await getAsarPath();
await loadConfig();
// Figure out the tray icon path & brand name
const iconFile = `icon.${process.platform === "win32" ? "ico" : "png"}`;
// we assume the resources path is in the same place as the asar
const resPath = await tryPaths("res", path.dirname(asarPath), [
// If run from the source checkout
"res",
// if run from packaged application
"",
]);
// The tray icon
// It's important to call `path.join` so we don't end up with the packaged asar in the final path.
const iconFile = `element.${process.platform === "win32" ? "ico" : "png"}`;
global.trayConfig = {
icon_path: path.join(path.dirname(asarPath), "build", iconFile),
icon_path: path.join(resPath, "img", iconFile),
brand: global.vectorConfig.brand || "Element",
};
// launcher
global.launcher = new AutoLaunch({
name: global.vectorConfig.brand || "Element",
isHidden: true,
mac: {
useLaunchAgent: true,
},
});
}
// Look for an auto-launcher under 'Riot' and if we find one,
// port its enabled/disabled-ness over to the new 'Element' launcher
async function moveAutoLauncher(): Promise<void> {
if (!global.vectorConfig.brand || global.vectorConfig.brand === "Element") {
const oldLauncher = new AutoLaunch({
name: "Riot",
isHidden: true,
mac: {
useLaunchAgent: true,
},
});
const wasEnabled = await oldLauncher.isEnabled();
if (wasEnabled) {
await oldLauncher.disable();
await global.launcher.enable();
}
}
}
global.appQuitting = false;
@@ -292,6 +302,11 @@ app.commandLine.appendSwitch("--enable-usermedia-screen-capturing");
if (!app.commandLine.hasSwitch("enable-features")) {
app.commandLine.appendSwitch("enable-features", "WebRTCPipeWireCapturer");
}
// Workaround bug in electron 36:https://github.com/electron/electron/issues/46538
// Hopefully this will no longer be needed soon and can be removed
if (process.platform === "linux") {
app.commandLine.appendSwitch("gtk-version", "3");
}
const gotLock = app.requestSingleInstanceLock();
if (!gotLock) {
@@ -348,6 +363,7 @@ app.on("ready", async () => {
try {
asarPath = await getAsarPath();
await setupGlobals();
await moveAutoLauncher();
} catch (e) {
console.log("App setup failed: exiting", e);
process.exit(1);
@@ -425,6 +441,7 @@ app.on("ready", async () => {
if (argv["update"] === false) {
console.log("Auto update disabled via command line flag");
} else if (global.vectorConfig["update_base_url"]) {
console.log(`Starting auto update with base URL: ${global.vectorConfig["update_base_url"]}`);
void updater.start(global.vectorConfig["update_base_url"]);
} else {
console.log("No update_base_url is defined: auto update is disabled");
@@ -436,6 +453,14 @@ app.on("ready", async () => {
store,
});
try {
console.debug("Ensuring storage is ready");
await store.safeStorageReady();
} catch (e) {
console.error(e);
app.exit(1);
}
// Load the previous window state with fallback to defaults
const mainWindowState = windowStateKeeper({
defaultWidth: 1024,
@@ -467,17 +492,6 @@ app.on("ready", async () => {
webgl: true,
},
});
global.mainWindow.setContentProtection(store.get("enableContentProtection"));
try {
console.debug("Ensuring storage is ready");
if (!(await store.prepareSafeStorage(global.mainWindow.webContents.session))) return;
} catch (e) {
console.error(e);
app.exit(1);
}
void global.mainWindow.loadURL("vector://vector/webapp/");
if (process.platform === "darwin") {
@@ -565,28 +579,10 @@ app.on("ready", async () => {
webContentsHandler(global.mainWindow.webContents);
session.defaultSession.setDisplayMediaRequestHandler(
(_, callback) => {
if (process.env.XDG_SESSION_TYPE === "wayland") {
// On Wayland, calling getSources() opens the xdg-desktop-portal picker.
// The user can only select a single source there, so Electron will return an array with exactly one entry.
desktopCapturer
.getSources({ types: ["screen", "window"] })
.then((sources) => {
callback({ video: sources[0] });
})
.catch((err) => {
// If the user cancels the dialog an error occurs "Failed to get sources"
console.error("Wayland: failed to get user-selected source:", err);
callback({ video: { id: "", name: "" } }); // The promise does not return if no dummy is passed here as source
});
} else {
global.mainWindow?.webContents.send("openDesktopCapturerSourcePicker");
}
setDisplayMediaCallback(callback);
},
{ useSystemPicker: true },
); // Use Mac OS 15+ native picker
session.defaultSession.setDisplayMediaRequestHandler((_, callback) => {
global.mainWindow?.webContents.send("openDesktopCapturerSourcePicker");
setDisplayMediaCallback(callback);
});
setupMediaAuth(global.mainWindow);
});

View File

@@ -32,22 +32,9 @@
"speech_start_speaking": "Spustit nahrávání hlasu",
"speech_stop_speaking": "Zastavit nahrávání hlasu"
},
"eol": {
"no_more_updates": "Používáte nepodporovanou verzi systému macOS. Prosím upgradujte %(brand)s pro získání aktualizací.",
"title": "Systém není podporován",
"warning": "Používáte nepodporovanou verzi systému macOS. Proveďte prosím upgrade %(brand)s , aby byl stále funkční."
},
"file_menu": {
"label": "Soubor"
},
"icon_overlay": {
"description_error": "Chyba",
"description_notifications": {
"one": "Máte %(count)s nepřečtené oznámení.",
"few": "Máte %(count)s nepřečtená oznámení.",
"other": "Máte %(count)s nepřečtených oznámení."
}
},
"menu": {
"hide": "Skrýt",
"hide_others": "Skrýt ostatní",
@@ -69,14 +56,13 @@
"backend_changed": "Vymazat data a znovu načíst?",
"backend_changed_detail": "Nelze získat přístup k tajnému klíči ze systémové klíčenky, zdá se, že se změnil.",
"backend_changed_title": "Nepodařilo se načíst databázi",
"backend_no_encryption": "Váš systém má podporovanou klíčenku, ale šifrování není k dispozici.",
"backend_no_encryption_detail": "Electron zjistil, že pro vaši klíčenku %(backend)s není k dispozici šifrování. Ujistěte se, že máte nainstalovanou klíčenku. Pokud ji máte, restartujte počítač a zkuste to znovu. Volitelně můžete povolit %(brand)s použít slabší formu šifrování.",
"backend_no_encryption_title": "Bez podpory šifrování",
"unknown_backend_override": "Váš systém má nepodporovanou klíčenku, což znamená, že databázi nelze otevřít.",
"unknown_backend_override_details": "Další podrobnosti naleznete v protokolech.",
"unknown_backend_override_title": "Nepodařilo se načíst databázi",
"unsupported_keyring": "Váš systém má nepodporovanou klíčenku, což znamená, že databázi nelze otevřít.",
"unsupported_keyring_cta": "Používat slabší šifrování",
"unsupported_keyring_detail": "Detekce klíčenky Electronu nenalezla podporovaný backend. Můžete se pokusit ručně nakonfigurovat backend spuštěním %(brand)s s argumentem příkazového řádku, jednorázovou operací. Viz %(link)s.",
"unsupported_keyring_title": "Systém není podporován",
"unsupported_keyring_use_basic_text": "Používat slabší šifrování",
"unsupported_keyring_use_plaintext": "Nepoužívat žádné šifrování"
"unsupported_keyring_title": "Systém není podporován"
}
},
"view_menu": {

View File

@@ -32,22 +32,9 @@
"speech_start_speaking": "Cychwyn Llefaru",
"speech_stop_speaking": "Peidio Llefaru"
},
"eol": {
"no_more_updates": "Rydych chi'n rhedeg fersiwn o macOS nad yw'n cael ei chefnogi. Uwchraddiwch i dderbyn diweddariadau %(brand)s.",
"title": "System heb ei chefnogi",
"warning": "Rydych chi'n rhedeg fersiwn o macOS nad yw'n cael ei chefnogi. Uwchraddiwch i sicrhau bod %(brand)s yn parhau i weithio."
},
"file_menu": {
"label": "Ffeil"
},
"icon_overlay": {
"description_error": "Gwall",
"description_notifications": {
"Mae gennych chi %(count)s hysbysiadau heb eu darllen.": "zero",
"Mae gennych chi %(count)s hysbysiad heb ei ddarllen.": "one",
"Mae gennych chi %(count)s hysbysiad heb eu darllen.": "other"
}
},
"menu": {
"hide": "Cuddio",
"hide_others": "Cuddio'r Gweddill",
@@ -72,11 +59,13 @@
"backend_no_encryption": "Mae gan eich system cylch allwedd sy'n cael ei gefnogi ond nid yw amgryptio ar gael.",
"backend_no_encryption_detail": "Mae Electron wedi canfod nad yw amgryptio ar gael ar eich cylch allweddi %(backend)s. Gwnewch yn siŵr bod y cylch allweddi wedi'i osod. Os oes y cylch allweddi wedi'i osod, ail gychwynnwch a cheisiwch eto. Yn ddewisol, gallwch ganiatáu i %(brand)s ddefnyddio ffurf wannach o amgryptio.",
"backend_no_encryption_title": "Dim cefnogaeth amgryptio",
"unknown_backend_override": "Mae gan eich system allweddell nad yw'n cael ei chefnogi sy'n golygu na ellir agor y gronfa ddata.",
"unknown_backend_override_details": "Gwiriwch y logiau am fwy o fanylion.",
"unknown_backend_override_title": "Methwyd llwytho'r gronfa ddata",
"unsupported_keyring": "Mae gan eich system allweddell nad yw'n cael ei chefnogi sy'n golygu nad oes modd agor y gronfa ddata.",
"unsupported_keyring_cta": "Defnyddiwch amgryptio gwannach",
"unsupported_keyring_detail": "Heb ganfod allweddell Electron gefn. Gallwch geisio ffurfweddu'r gefn â llaw trwy gychwyn %(brand)s gyda dadl llinell orchymyn, gweithrediad untro. Gweler %(link)s.",
"unsupported_keyring_title": "System heb ei chefnogi",
"unsupported_keyring_use_basic_text": "Defnyddiwch amgryptio gwannach",
"unsupported_keyring_use_plaintext": "Peidiwch â defnyddio amgryptio"
"unsupported_keyring_title": "System heb ei chefnogi"
}
},
"view_menu": {

View File

@@ -32,21 +32,9 @@
"speech_start_speaking": "Aufnahme starten",
"speech_stop_speaking": "Aufnahme beenden"
},
"eol": {
"no_more_updates": "Du benutzt eine nicht unterstützte Version von macOS. Bitte aktualisiere, um Updates für %(brand)s zu erhalten.",
"title": "System nicht unterstützt",
"warning": "Du benutzt eine nicht unterstützte Version von macOS. Bitte aktualisiere, damit %(brand)s weiter funktioniert."
},
"file_menu": {
"label": "Datei"
},
"icon_overlay": {
"description_error": "Fehler",
"description_notifications": {
"one": "Du hast %(count)s ungelesene Benachrichtigung.",
"other": "Du hast %(count)s ungelesene Benachrichtigungen."
}
},
"menu": {
"hide": "Verstecken",
"hide_others": "Andere verstecken",
@@ -68,14 +56,13 @@
"backend_changed": "Daten löschen und neu laden?",
"backend_changed_detail": "Zugriff auf Schlüssel im Systemschlüsselbund nicht möglich, er scheint sich geändert zu haben.",
"backend_changed_title": "Datenbank konnte nicht geladen werden",
"backend_no_encryption": "Ihr System verfügt über einen unterstützten Keyring, aber die Verschlüsselung ist nicht verfügbar.",
"backend_no_encryption_detail": "Electron hat festgestellt, dass der Keyring von %(backend)s keine Verschlüsselung bietet. Wenn du den Keyring installiert hast, starte den Rechner bitte neu und versuche es erneut. Optional kann %(brand)s auch eine abgeschwächte Verschlüsselung nutzen.",
"backend_no_encryption_title": "Keine Verschlüsselungsunterstützung",
"unsupported_keyring": "Der Keyring des Systems wird nicht unterstützt. Daher kann die Datenbank nicht geöffnet werden kann.",
"unsupported_keyring_detail": "Die Keyring-Erkennung von Electron hat kein unterstütztes Backend gefunden. Du kannst einmalig versuchen, eine manuelle Konfiguration von %(brand)s über die Kommandozeile vorzunehmen. Infos unter %(link)s.",
"unsupported_keyring_title": "System nicht unterstützt",
"unsupported_keyring_use_basic_text": "Schwächere Verschlüsselung verwenden",
"unsupported_keyring_use_plaintext": "Verwende keine Verschlüsselung"
"unknown_backend_override": "Der Schlüsselbund ihres Systems wird nicht unterstützt, wodurch die Datenbank nicht geöffnet werden kann.",
"unknown_backend_override_details": "Weitere Informationen finden Sie in den Protokollen.",
"unknown_backend_override_title": "Die Datenbank konnte nicht geladen werden",
"unsupported_keyring": "Der Schlüsselbund ihres Systems wird nicht unterstützt, wodurch die Datenbank nicht geöffnet werden kann.",
"unsupported_keyring_cta": "Schwächere Verschlüsselung verwenden",
"unsupported_keyring_detail": "Die Schlüsselbunderkennung von Electron hat kein unterstütztes Backend gefunden. Möglicherweise können sie dennoch den ihres Systemes verwenden. Infos unter %(link)s.",
"unsupported_keyring_title": "System nicht unterstützt"
}
},
"view_menu": {

View File

@@ -32,21 +32,9 @@
"speech_start_speaking": "Start Speaking",
"speech_stop_speaking": "Stop Speaking"
},
"eol": {
"no_more_updates": "You are running an unsupported version of macOS. Please upgrade to receive %(brand)s updates.",
"title": "System unsupported",
"warning": "You are running an unsupported version of macOS. Please upgrade to ensure %(brand)s keeps working."
},
"file_menu": {
"label": "File"
},
"icon_overlay": {
"description_error": "Error",
"description_notifications": {
"one": "You have %(count)s unread notification.",
"other": "You have %(count)s unread notifications."
}
},
"menu": {
"hide": "Hide",
"hide_others": "Hide Others",
@@ -71,11 +59,13 @@
"backend_no_encryption": "Your system has a supported keyring but encryption is not available.",
"backend_no_encryption_detail": "Electron has detected that encryption is not available on your keyring %(backend)s. Please ensure that you have the keyring installed. If you do have the keyring installed, please reboot and try again. Optionally, you can allow %(brand)s to use a weaker form of encryption.",
"backend_no_encryption_title": "No encryption support",
"unknown_backend_override": "Your system has an unsupported keyring meaning the database cannot be opened.",
"unknown_backend_override_details": "Please check the logs for more details.",
"unknown_backend_override_title": "Failed to load database",
"unsupported_keyring": "Your system has an unsupported keyring meaning the database cannot be opened.",
"unsupported_keyring_cta": "Use weaker encryption",
"unsupported_keyring_detail": "Electron's keyring detection did not find a supported backend. You can attempt to manually configure the backend by starting %(brand)s with a command-line argument, a one-time operation. See %(link)s.",
"unsupported_keyring_title": "System unsupported",
"unsupported_keyring_use_basic_text": "Use weaker encryption",
"unsupported_keyring_use_plaintext": "Use no encryption"
"unsupported_keyring_title": "System unsupported"
}
},
"view_menu": {

View File

@@ -32,21 +32,9 @@
"speech_start_speaking": "Alusta rääkimist",
"speech_stop_speaking": "Lõpeta rääkimine"
},
"eol": {
"no_more_updates": "Sa kasutad macOS-i toetamata versiooni. %(brand)s rakenduse tulevaste versioonide kasutamiseks palun uuenda operatsioonisüsteemi.",
"title": "Süsteem pole toetatud",
"warning": "Sa kasutad macOS-i toetamata versiooni. Et %(brand)s toimiks ka edaspidi, palun uuenda operatsioonisüsteemi."
},
"file_menu": {
"label": "Fail"
},
"icon_overlay": {
"description_error": "Viga",
"description_notifications": {
"one": "Sul on %(count)s lugemata teavitus",
"other": "Sul on %(count)s lugemata teavitust"
}
},
"menu": {
"hide": "Peida",
"hide_others": "Peida muud",
@@ -71,11 +59,13 @@
"backend_no_encryption": "Sinu süsteem kasutab toetatud võtmerõngast, kuid krüptimist pole saadaval.",
"backend_no_encryption_detail": "Electron on tuvastanud, et krüptimine pole sinu %(backend)s võtmerõnga jaoks saadaval. Palun kontrolli, et võtmerõngas oleks korrektselt paigaldatud. Kui sul on võtmerõngas paigaldatud, siis palun taaskäivita ta ja proovi uuesti. Lisavõimalusena saad lubada, et %(brand)s kasutab nõrgemat krüptimislahendust.",
"backend_no_encryption_title": "Krüptimise tugi puudub",
"unknown_backend_override": "Sinu süsteemis on kasutusel mittetoetatud võtmerõnga versioon ning see tähendab, et andmebaasi ei saa avada.",
"unknown_backend_override_details": "Lisateavet leiad logidest.",
"unknown_backend_override_title": "Andmebaasi ei õnnestunud laadida",
"unsupported_keyring": "Sinu süsteemis on kasutusel mittetoetatud võtmerõnga versioon ning see tähendab, et andmebaasi ei saa avada.",
"unsupported_keyring_cta": "Kasuta nõrgemat krüptimist",
"unsupported_keyring_detail": "Electroni võtmerõnga tuvastamine ei leidnud toetatud taustateenust. Kui käivitad rakenduse %(brand)s käsurealt õigete argumentidega, siis võib taustateenuse käsitsi seadistamine õnnestuda ning seda tegevust peaksid vaid üks kord tegema. Lisateave: %(link)s.",
"unsupported_keyring_title": "Süsteem pole toetatud",
"unsupported_keyring_use_basic_text": "Kasuta nõrgemat krüptimist",
"unsupported_keyring_use_plaintext": "Ära üldse kasuta krüptimist"
"unsupported_keyring_title": "Süsteem pole toetatud"
}
},
"view_menu": {

View File

@@ -54,6 +54,9 @@
"store": {
"error": {
"backend_changed_title": "Tietokannan lataaminen epäonnistui",
"unknown_backend_override_details": "Katso tarkemmat tiedot lokeista.",
"unknown_backend_override_title": "Tietokannan lataaminen epäonnistui",
"unsupported_keyring_cta": "Käytä heikompaa salausta",
"unsupported_keyring_title": "Järjestelmä ei ole tuettu"
}
},

View File

@@ -32,21 +32,9 @@
"speech_start_speaking": "Commencer la dictée",
"speech_stop_speaking": "Arrêter la dictée"
},
"eol": {
"no_more_updates": "Vous utilisez une version de macOS non prise en charge. Veuillez mettre à jour macOS pour recevoir les mises à jour de %(brand)s.",
"title": "Système non pris en charge",
"warning": "Vous utilisez une version de macOS non prise en charge. Veuillez mettre à jour macOS afin que %(brand)s continue de fonctionner."
},
"file_menu": {
"label": "Fichier"
},
"icon_overlay": {
"description_error": "Erreur",
"description_notifications": {
"one": "Vous avez %(count)s notification non lue.",
"other": "Vous avez %(count)s notifications non lues."
}
},
"menu": {
"hide": "Masquer",
"hide_others": "Masquer les autres",
@@ -60,7 +48,7 @@
"copy_image_url": "Copier l'adresse de l'image",
"copy_link_url": "Copier ladresse du lien",
"save_image_as": "Enregistrer limage sous…",
"save_image_as_error_description": "Limage na pas pu être enregistrée",
"save_image_as_error_description": "Limage na pas pu être sauvegardée",
"save_image_as_error_title": "Échec de la sauvegarde de limage"
},
"store": {
@@ -68,14 +56,13 @@
"backend_changed": "Effacer les données et recharger ?",
"backend_changed_detail": "Impossible d'accéder aux secrets depuis le trousseau de clés du système, il semble avoir changé.",
"backend_changed_title": "Impossible de charger la base de données",
"backend_no_encryption": "Votre système dispose d'un trousseau de clés compatible mais le chiffrement est indisponible.",
"backend_no_encryption_detail": "Electron a détecté que le chiffrement n'est pas disponible pour le trousseau %(backend)s. Veuillez vérifier que votre trousseau est installé. Si c'est le cas, redémarrez et réessayez. Comme alternative, vous pouvez autoriser %(brand)s à utiliser une option de chiffrement moins sécurisée.",
"backend_no_encryption_title": "Aucune prise en charge du chiffrement",
"unknown_backend_override": "Votre système possède un trousseau de clés non pris en charge, ce qui signifie que la base de données ne peut pas être ouverte.",
"unknown_backend_override_details": "Veuillez consulter les journaux pour plus de détails.",
"unknown_backend_override_title": "Impossible de charger la base de données",
"unsupported_keyring": "Votre système possède un trousseau de clés non pris en charge, la base de données ne peut pas être ouverte.",
"unsupported_keyring_cta": "Utiliser un chiffrement plus faible",
"unsupported_keyring_detail": "La détection du porte-clés par Electron n'a pas permis de trouver de backend compatible. Vous pouvez essayer de configurer manuellement le backend en utilisant %(brand)s avec un argument de ligne de commande. Cette opération doit être effectuer une seule fois. Voir%(link)s.",
"unsupported_keyring_title": "Système non pris en charge",
"unsupported_keyring_use_basic_text": "Utiliser un chiffrement plus faible",
"unsupported_keyring_use_plaintext": "N'utilise pas de chiffrement"
"unsupported_keyring_title": "Système non pris en charge"
}
},
"view_menu": {

View File

@@ -32,21 +32,9 @@
"speech_start_speaking": "Kezdjen beszélni",
"speech_stop_speaking": "Fejezze be a beszédet"
},
"eol": {
"no_more_updates": "A macOS egy nem támogatott verzióját futtatja. Frissítse, hogy megkapja az %(brand)s új frissítéseit.",
"title": "A rendszer nem támogatott",
"warning": "A macOS egy nem támogatott verzióját futtatja. Frissítse a rendszert, hogy biztosítsa az %(brand)s további működését."
},
"file_menu": {
"label": "Fájl"
},
"icon_overlay": {
"description_error": "Hiba",
"description_notifications": {
"one": "%(count)s olvasatlan értesítése van.",
"other": "%(count)s olvasatlan értesítése van."
}
},
"menu": {
"hide": "Elrejtés",
"hide_others": "Mások elrejtése",
@@ -68,14 +56,13 @@
"backend_changed": "Adatok törlése és újratöltés?",
"backend_changed_detail": "Nem sikerült hozzáférni a rendszerkulcstartó titkos kódjához, úgy tűnik, megváltozott.",
"backend_changed_title": "Nem sikerült betölteni az adatbázist",
"backend_no_encryption": "A rendszer támogatott kulcstartóval rendelkezik, de a titkosítás nem érhető el.",
"backend_no_encryption_detail": "Az Electron észlelte, hogy a titkosítás nem érhető el a(z) %(backend)s kulcstartóján. Győződjön meg róla, hogy telepítve van-e a kulcstartó. Ha telepítve van, indítsa újra és próbálja újra. Esetleg engedélyezheti a gyengébb titkosítást az %(brand)s számára.",
"backend_no_encryption_title": "Nincs titkosítási támogatás",
"unknown_backend_override": "A rendszer nem támogatott kulcstartóval rendelkezik, ami azt jelenti, hogy az adatbázis nem nyitható meg.",
"unknown_backend_override_details": "További részletekért ellenőrizze a naplókat.",
"unknown_backend_override_title": "Nem sikerült betölteni az adatbázist",
"unsupported_keyring": "A rendszer nem támogatott kulcstartóval rendelkezik, ami azt jelenti, hogy az adatbázis nem nyitható meg.",
"unsupported_keyring_cta": "Gyengébb titkosítás használata",
"unsupported_keyring_detail": "Az Electron kulcstartóészlelése nem talált támogatott háttérrendszert. Megpróbálhatja kézileg beállítani a háttérrendszert az %(brand)s egyszeri, parancssori argumentummal való indításával. Lásd: %(link)s.",
"unsupported_keyring_title": "A rendszer nem támogatott",
"unsupported_keyring_use_basic_text": "Gyengébb titkosítás használata",
"unsupported_keyring_use_plaintext": "Ne használjon titkosítást"
"unsupported_keyring_title": "A rendszer nem támogatott"
}
},
"view_menu": {

View File

@@ -1,87 +0,0 @@
{
"action": {
"cancel": "Չեղարկել",
"close": "Փակել",
"close_brand": "Փակել %(brand)s",
"copy": "Պատճենել",
"cut": "Կտրել",
"delete": "Ջնջել",
"edit": "Խմբագրել",
"minimise": "Նվազագույնի հասցնել",
"paste": "Տեղադրել",
"paste_match_style": "Տեղադրել և համապատասխանեցնել ոճը",
"quit": "Դուրս գալ",
"redo": "Կրկնել",
"select_all": "Ընտրել բոլորը",
"show_hide": "Ցուցադրել/Թաքցնել",
"undo": "Հետարկել",
"zoom_in": "Մեծացնել",
"zoom_out": "Փոքրացնել"
},
"common": {
"about": "Կենսագրություն",
"brand_help": "%(brand)s Օգնություն",
"help": "Օգնություն",
"no": "Ոչ",
"preferences": "Նախապատվություններ",
"yes": "Այո"
},
"confirm_quit": "Վստա՞հ եք, որ ուզում եք դուրս գալ։",
"edit_menu": {
"speech": "Խոսք/Ելույթ",
"speech_start_speaking": "Սկսեք խոսել",
"speech_stop_speaking": "Դադարեցրեք խոսելը"
},
"file_menu": {
"label": "Ֆայլ"
},
"icon_overlay": {
"description_error": "Սխալ",
"description_notifications": {
"one": "Դուք ունեք %(count)s չկարդացված ծանուցում։",
"other": "Դուք ունեք %(count)s չկարդացված ծանուցումներ։"
}
},
"menu": {
"hide": "Թաքցնել",
"hide_others": "Թաքցնել մյուսները",
"services": "Ծառայություններ",
"unhide": "Ապաթաքցնել"
},
"right_click_menu": {
"add_to_dictionary": "Ավելացնել բառարանում",
"copy_email": "Պատճենել էլ․ հասցեն",
"copy_image": "Պատճենել պատկերը",
"copy_image_url": "Պատճենել պատկերի հասցեն",
"copy_link_url": "Պատճենել հղման հասցեն",
"save_image_as": "Պահպանել պատկերը որպես...",
"save_image_as_error_description": "Պատկերը չհաջողվեց պահպանել",
"save_image_as_error_title": "Չհաջողվեց պահպանել պատկերը"
},
"store": {
"error": {
"backend_changed": "Մաքրե՞լ տվյալները և վերաբեռնե՞լ",
"backend_changed_detail": "Գաղտնի տվյալները հասանելի չեն համակարգի բանալիների պահոցից(keyring), կարծես թե այն փոխվել է։",
"backend_changed_title": "Չհաջողվեց բեռնել տվյալների բազան",
"backend_no_encryption": "Ձեր համակարգը ունի աջակցվող բանալիների պահոց, բայց գաղտնագրումը հասանելի չէ։",
"backend_no_encryption_detail": "Electron-ը հայտնաբերել է, որ ձեր բանալիների պահոցում %(backend)s գաղտնագրումը հասանելի չէ։ Խնդրում ենք համոզվել, որ բանալիների պահոցը տեղադրված է։ Եթե այն արդեն տեղադրված է, վերագործարկեք համակարգը և փորձեք կրկին։ Ցանկության դեպքում կարող եք թույլատրել %(brand)s-ին կիրառել ավելի թույլ գաղտնագրման տարբերակ։",
"backend_no_encryption_title": "Գաղտնագրման աջակցություն չկա",
"unsupported_keyring": "Համակարգում օգտագործվող բանալիների պահոցը(keyring) չի աջակցվում, ինչը նշանակում է, որ տվյալների բազան հնարավոր չէ բացել։",
"unsupported_keyring_detail": "Electron-ի բանալիների պահոցի ստուգումը չգտավ համատեղելի backend։ Կարող եք փորձել ձեռքով կարգավորել backend-ը` մեկնարկելով %(brand)s-ը command-line արգումենտով, մեկանգամյա գործողությամբ։ Տես %(link)s։",
"unsupported_keyring_title": "Համակարգը չի աջակցվում",
"unsupported_keyring_use_basic_text": "Օգտագործել ավելի թույլ գաղտնագրում",
"unsupported_keyring_use_plaintext": "Չօգտագործել գաղտնագրում"
}
},
"view_menu": {
"actual_size": "Իրական չափս",
"toggle_developer_tools": "Միացնել/անջատել ծրագրավորողի գործիքները",
"toggle_full_screen": "Միացնել/անջատել ամբողջական էկրանը",
"view": "Դիտել"
},
"window_menu": {
"bring_all_to_front": "Բերեք բոլորին առջևի պլան",
"label": "Պատուհան",
"zoom": "Մեծացնել"
}
}

View File

@@ -32,20 +32,9 @@
"speech_start_speaking": "Mulai Berbicara",
"speech_stop_speaking": "Berhenti Berbicara"
},
"eol": {
"no_more_updates": "Anda menggunakan versi macOS yang tidak didukung. Harap tingkatkan untuk menerima pembaruan %(brand)s.",
"title": "Sistem tidak didukung",
"warning": "Anda menggunakan versi macOS yang tidak didukung. Harap perbarui untuk memastikan%(brand)s terus bekerja."
},
"file_menu": {
"label": "Berkas"
},
"icon_overlay": {
"description_error": "Kesalahan",
"description_notifications": {
"other": "Anda memiliki %(count)s notifikasi yang belum dibaca."
}
},
"menu": {
"hide": "Sembunyikan",
"hide_others": "Sembunyikan yang Lain",
@@ -67,14 +56,13 @@
"backend_changed": "Hapus data dan muat ulang?",
"backend_changed_detail": "Tidak dapat mengakses rahasia dari keyring sistem, tampaknya telah berubah.",
"backend_changed_title": "Gagal memuat basis data",
"backend_no_encryption": "Sistem Anda memiliki keyring yang didukung tetapi enkripsi tidak tersedia.",
"backend_no_encryption_detail": "Electron telah mendeteksi bahwa enkripsi tidak tersedia pada keyring %(backend)s Anda. Harap pastikan bahwa Anda telah memasang keyring. Jika Anda telah memasang keyring, silakan mulai ulang dan coba lagi. Secara opsional, Anda dapat mengizinkan %(brand)s untuk menggunakan bentuk enkripsi yang lebih lemah.",
"backend_no_encryption_title": "Tidak ada dukungan enkripsi",
"unknown_backend_override": "Sistem Anda memiliki keyring yang tidak didukung yang berarti basis data tidak dapat dibuka.",
"unknown_backend_override_details": "Silakan periksa log untuk detail lebih lanjut.",
"unknown_backend_override_title": "Gagal memuat basis data",
"unsupported_keyring": "Sistem Anda memiliki keyring yang tidak didukung yang berarti basis data tidak dapat dibuka.",
"unsupported_keyring_cta": "Gunakan enkripsi yang lebih lemah",
"unsupported_keyring_detail": "Deteksi keyring Electron tidak menemukan backend yang didukung. Anda dapat mencoba mengonfigurasi backend secara manual dengan memulai %(brand)s dengan argumen baris perintah, operasi satu kali. Lihat %(link)s.",
"unsupported_keyring_title": "Sistem tidak didukung",
"unsupported_keyring_use_basic_text": "Gunakan enkripsi yang lebih lemah",
"unsupported_keyring_use_plaintext": "Jangan gunakan enkripsi"
"unsupported_keyring_title": "Sistem tidak didukung"
}
},
"view_menu": {

View File

@@ -23,7 +23,7 @@
"brand_help": "%(brand)s Hjelp",
"help": "Hjelp",
"no": "Nei",
"preferences": "Innstillinger",
"preferences": "Brukervalg",
"yes": "Ja"
},
"confirm_quit": "Er du sikker på at du vil slutte?",
@@ -32,21 +32,9 @@
"speech_start_speaking": "Begynn å snakke",
"speech_stop_speaking": "Slutt å snakke"
},
"eol": {
"no_more_updates": "Du kjører en versjon av macOS som ikke støttes. Oppgrader for å motta oppdateringer fr %(brand)s.",
"title": "Systemet støttes ikke",
"warning": "Du bruker en versjon av macOS som ikke støttes. Oppgrader for å sikre at %(brand)s fortsetter å fungere."
},
"file_menu": {
"label": "Fil"
},
"icon_overlay": {
"description_error": "Feil",
"description_notifications": {
"one": "Du har %(count)s ulest varsel.",
"other": "Du har %(count)s uleste varsler."
}
},
"menu": {
"hide": "Skjul",
"hide_others": "Skjul andre",
@@ -71,11 +59,13 @@
"backend_no_encryption": "Systemet ditt har en støttet nøkkelring, men kryptering er ikke tilgjengelig.",
"backend_no_encryption_detail": "Electron har oppdaget at kryptering ikke er tilgjengelig på nøkkelringen %(backend)s din. Forsikre deg om at du har nøkkelringen installert. Hvis du har nøkkelringen installert, vennligst start på nytt og prøv igjen. Eventuelt kan du tillate %(brand)s å bruke en svakere form for kryptering.",
"backend_no_encryption_title": "Ingen støtte for kryptering",
"unknown_backend_override": "Systemet ditt har en nøkkelring som ikke støttes, noe som betyr at databasen ikke kan åpnes.",
"unknown_backend_override_details": "Vennligst sjekk loggene for mer informasjon.",
"unknown_backend_override_title": "Kunne ikke laste inn databasen",
"unsupported_keyring": "Systemet ditt har en nøkkelring som ikke støttes, noe som betyr at databasen ikke kan åpnes.",
"unsupported_keyring_cta": "Bruk svakere kryptering",
"unsupported_keyring_detail": "Electrons nøkkelringdeteksjon fant ikke en støttet backend. Du kan prøve å konfigurere backend manuelt ved å starte %(brand)s med et kommandolinjeargument, en engangsoperasjon. Se%(link)s.",
"unsupported_keyring_title": "Systemet støttes ikke",
"unsupported_keyring_use_basic_text": "Bruk svakere kryptering",
"unsupported_keyring_use_plaintext": "Ikke bruk kryptering"
"unsupported_keyring_title": "Systemet støttes ikke"
}
},
"view_menu": {

View File

@@ -32,22 +32,9 @@
"speech_start_speaking": "Zacznij mówić",
"speech_stop_speaking": "Przestań mówić"
},
"eol": {
"no_more_updates": "Korzystasz z nieobsługiwanej wersji systemu macOS. Zaktualizuj system, aby uzyskać aktualizacje %(brand)s.",
"title": "System nie jest obsługiwany",
"warning": "Korzystasz z nieobsługiwanej wersji systemu macOS. Zaktualizuj system, aby dalej korzystać z %(brand)s."
},
"file_menu": {
"label": "Plik"
},
"icon_overlay": {
"description_error": "Błąd",
"description_notifications": {
"one": "Masz %(count)s nieprzeczytane powiadomienie.",
"few": "Masz %(count)s nieprzeczytane powiadomienia.",
"many": "Masz %(count)s nieprzeczytanych powiadomień."
}
},
"menu": {
"hide": "Ukryj",
"hide_others": "Ukryj inne",
@@ -69,14 +56,13 @@
"backend_changed": "Wyczyścić dane i przeładować?",
"backend_changed_detail": "Nie można uzyskać dostępu do sekretnego magazynu, wygląda na to, że uległ zmianie.",
"backend_changed_title": "Nie udało się załadować bazy danych",
"backend_no_encryption": "Twój system posiada wspierany keyring, ale szyfrowanie nie jest dostępne.",
"backend_no_encryption_detail": "Elektron wykrył, że szyfrowanie nie jest dostępne w twoim keyring'u %(backend)s. Upewnij się, że keyring został zainstalowany. Jeśli tak, uruchom ponownie urządzenie i spróbuj ponownie. Opcjonalnie, zezwól %(brand)s, aby korzystał ze słabszego szyfrowania.",
"backend_no_encryption_title": "Szyfrowanie nie jest obsługiwane",
"unknown_backend_override": "System zawiera niewspierany keyring, nie można otworzyć bazy danych.",
"unknown_backend_override_details": "Sprawdź dziennik, aby uzyskać więcej informacji.",
"unknown_backend_override_title": "Nie udało się załadować bazy danych",
"unsupported_keyring": "System zawiera niewspierany keyring, nie można otworzyć bazy danych.",
"unsupported_keyring_cta": "Użyj słabszego szyfrowania",
"unsupported_keyring_detail": "Wykrywanie keyringu Electron nie znalazł wspieranego backendu. Możesz spróbować ręcznie ustawić backed, uruchamiając %(brand)s za pomocą wiesza poleceń. Zobacz %(link)s.",
"unsupported_keyring_title": "System niewspierany",
"unsupported_keyring_use_basic_text": "Użyj słabszego szyfrowania",
"unsupported_keyring_use_plaintext": "Nie używaj szyfrowania"
"unsupported_keyring_title": "System niewspierany"
}
},
"view_menu": {

View File

@@ -32,21 +32,9 @@
"speech_start_speaking": "Começar a Falar",
"speech_stop_speaking": "Parar de Falar"
},
"eol": {
"no_more_updates": "Você está usando uma versão não suportada do macOS. Atualize para receber as atualizações d %(brand)s.",
"title": "Sistema não suportado",
"warning": "Você está usando uma versão não compatível do macOS. Faça a atualização para garantir que o %(brand)s continue funcionando."
},
"file_menu": {
"label": "Arquivo"
},
"icon_overlay": {
"description_error": "Erro",
"description_notifications": {
"one": "Você tem %(count)s notificação não lida.",
"other": "Você tem %(count)s notificações não lidas."
}
},
"menu": {
"hide": "Esconder",
"hide_others": "Esconder Outras(os)",
@@ -71,11 +59,13 @@
"backend_no_encryption": "Seu sistema tem um cofre compatível, mas a criptografia não está disponível.",
"backend_no_encryption_detail": "O Electron detetou que a encriptação não está disponível no seu cofre %(backend)s. Certifique-se de que tem o cofre instalado. Se tiver o cofre instalado, reinicie e tente novamente. Opcionalmente, você pode permitir que %(brand)s use uma forma mais fraca de criptografia.",
"backend_no_encryption_title": "Sem suporte para criptografia",
"unknown_backend_override": "Seu sistema possui um cofre não compatível, o que impede a abertura do banco de dados.",
"unknown_backend_override_details": "Verifique os logs para obter mais detalhes.",
"unknown_backend_override_title": "Falha ao carregar o banco de dados",
"unsupported_keyring": "Seu sistema possui um cofre não compatível, o que impede a abertura do banco de dados.",
"unsupported_keyring_cta": "Use criptografia mais fraca",
"unsupported_keyring_detail": "A detecção de cofre do Electron não encontrou um backend compatível. Você pode tentar configurar manualmente o backend iniciando %(brand)s com um argumento de linha de comando, uma operação única. Consulte %(link)s.",
"unsupported_keyring_title": "Sistema não suportado",
"unsupported_keyring_use_basic_text": "Use criptografia mais fraca",
"unsupported_keyring_use_plaintext": "Não usar criptografia"
"unsupported_keyring_title": "Sistema não suportado"
}
},
"view_menu": {

View File

@@ -22,9 +22,7 @@
"about": "О программе",
"brand_help": "Помощь %(brand)s",
"help": "Помощь",
"no": "Нет",
"preferences": "Предпочтения",
"yes": "Да"
"preferences": "Предпочтения"
},
"confirm_quit": "Вы уверены, что хотите выйти?",
"edit_menu": {
@@ -32,22 +30,9 @@
"speech_start_speaking": "Говорите",
"speech_stop_speaking": "Перестаньте говорить"
},
"eol": {
"no_more_updates": "Вы используете неподдерживаемую версию macOS. Пожалуйста обновите систему чтобы получать обновления %(brand)s.",
"title": "Система не поддерживается",
"warning": "Вы используете неподдерживаемую версию macOS. Пожалуйста обновите её, чтобы %(brand)s продолжал работать."
},
"file_menu": {
"label": "Файл"
},
"icon_overlay": {
"description_error": "Ошибка",
"description_notifications": {
"one": "У вас есть %(count)s непрочитанное уведомление.",
"few": "У вас есть %(count)s непрочитанных уведомления.",
"many": "У вас есть %(count)s непрочитанных уведомлений."
}
},
"menu": {
"hide": "Скрыть",
"hide_others": "Скрыть прочие",
@@ -64,21 +49,6 @@
"save_image_as_error_description": "Не удалось сохранить изображение",
"save_image_as_error_title": "Не удалось сохранить изображение"
},
"store": {
"error": {
"backend_changed": "Очистить данные и перезагрузить?",
"backend_changed_detail": "Не удалось получить доступ к секрету из системной связки ключей. Похоже, что она изменилась.",
"backend_changed_title": "Не удалось загрузить базу данных",
"backend_no_encryption": "В вашей системе имеется поддерживаемая связка ключей, но шифрование недоступно.",
"backend_no_encryption_detail": "Electron обнаружил, что шифрование недоступно в вашем хранилище %(backend)s Убедитесь, что у вас установлено связное устройство. Если связное устройство установлено, перезагрузите компьютер и повторите попытку. При желании вы можете разрешить %(brand)s использовать более слабую форму шифрования.",
"backend_no_encryption_title": "Нет поддержки шифрования",
"unsupported_keyring": "Невозможно открыть базу данных, так как в вашей системе установлена неподдерживаемая связка ключей",
"unsupported_keyring_detail": "Функция обнаружения ключей Electron не нашла поддерживаемый сервер. Можно попробовать настроить сервер вручную, запустив %(brand)s с аргументом командной строки, это нужно сделать только один раз. Смотри %(link)s.",
"unsupported_keyring_title": "Система не поддерживается",
"unsupported_keyring_use_basic_text": "Использовать более слабое шифрование",
"unsupported_keyring_use_plaintext": "Не использовать шифрование"
}
},
"view_menu": {
"actual_size": "Фактический размер",
"toggle_developer_tools": "Переключить инструменты разработчика",

View File

@@ -32,22 +32,9 @@
"speech_start_speaking": "Spustiť nahrávanie hlasu",
"speech_stop_speaking": "Zastaviť nahrávanie hlasu"
},
"eol": {
"no_more_updates": "Používate nepodporovanú verziu systému macOS. Prosím, aktualizujte systém, aby ste mohli dostávať aktualizácie aplikácie %(brand)s.",
"title": "Systém nie je podporovaný",
"warning": "Používate nepodporovanú verziu systému macOS. Vykonajte prosím aktualizáciu, aby aplikácia %(brand)s mohla správne fungovať."
},
"file_menu": {
"label": "Súbor"
},
"icon_overlay": {
"description_error": "Chyba",
"description_notifications": {
"one": "Máte %(count)s neprečítané oznámenie.",
"few": "Máte %(count)s neprečítané oznámenia.",
"other": "Máte %(count)s neprečítaných oznámení."
}
},
"menu": {
"hide": "Skryť",
"hide_others": "Skryť ostatné",
@@ -72,11 +59,13 @@
"backend_no_encryption": "Váš systém má podporovaný zväzok kľúčov, ale šifrovanie nie je k dispozícii.",
"backend_no_encryption_detail": "Electron zistil, že šifrovanie nie je k dispozícii na vašom zväzku kľúčov %(backend)s. Uistite sa, že máte nainštalovaný zväzok kľúčov. Ak máte zväzok kľúčov nainštalovaný, reštartujte počítač a skúste to znova. Voliteľne môžete povoliť aplikácii %(brand)s používať slabšiu formu šifrovania.",
"backend_no_encryption_title": "Žiadna podpora šifrovania",
"unknown_backend_override": "Váš systém má nepodporovaný zväzok kľúčov, čo znamená, že databázu nemožno otvoriť.",
"unknown_backend_override_details": "Pre viac informácií si pozrite protokoly.",
"unknown_backend_override_title": "Nepodarilo sa načítať databázu",
"unsupported_keyring": "Váš systém má nepodporovaný zväzok kľúčov, čo znamená, že databázu nemožno otvoriť.",
"unsupported_keyring_cta": "Použiť slabšie šifrovanie",
"unsupported_keyring_detail": "Detekcia zväzku kľúčov aplikácie Electron nenašla podporovaný backend. Môžete sa pokúsiť manuálne nastaviť backend spustením aplikácie %(brand)s s argumentom príkazového riadka, je to jednorazová operácia. Pozrite si %(link)s .",
"unsupported_keyring_title": "Systém nie je podporovaný",
"unsupported_keyring_use_basic_text": "Použiť slabšie šifrovanie",
"unsupported_keyring_use_plaintext": "Nepoužiť žiadne šifrovanie"
"unsupported_keyring_title": "Systém nie je podporovaný"
}
},
"view_menu": {

View File

@@ -22,9 +22,7 @@
"about": "Om",
"brand_help": "%(brand)s-hjälp",
"help": "Hjälp",
"no": "Nej",
"preferences": "Inställningar",
"yes": "Ja"
"preferences": "Inställningar"
},
"confirm_quit": "Är du säker att du vill avsluta?",
"edit_menu": {
@@ -35,13 +33,6 @@
"file_menu": {
"label": "Arkiv"
},
"icon_overlay": {
"description_error": "Fel",
"description_notifications": {
"one": "Du har %(count)s oläst avisering.",
"other": "Du har %(count)s olästa aviseringar."
}
},
"menu": {
"hide": "Göm",
"hide_others": "Göm övriga",
@@ -58,21 +49,6 @@
"save_image_as_error_description": "Bilden sparades inte",
"save_image_as_error_title": "Misslyckades med att spara bilden"
},
"store": {
"error": {
"backend_changed": "Rensa data och ladda om?",
"backend_changed_detail": "Kunde inte komma åt hemligheten från systemnyckelringen, det verkar ha ändrats.",
"backend_changed_title": "Misslyckades att ladda databasen",
"backend_no_encryption": "Ditt system har en nyckelring som stöds men kryptering är inte tillgänglig.",
"backend_no_encryption_detail": "Electron har upptäckt att kryptering inte är tillgänglig på din nyckelring %(backend)s. Se till att du har nyckelringen installerad. Om du har nyckelringen installerad, starta om och försök igen. Alternativt kan du tillåta %(brand)s att använda en svagare form av kryptering.",
"backend_no_encryption_title": "Inget krypteringsstöd",
"unsupported_keyring": "Ditt system har en nyckelring som inte stöds, vilket innebär att databasen inte kan öppnas.",
"unsupported_keyring_detail": "Electrons nyckelringsdetektering hittade inte en backend som stöds. Du kan försöka konfigurera backend manuellt genom att starta %(brand)s med ett kommandoradsargument, en engångsåtgärd. Se %(link)s.",
"unsupported_keyring_title": "Systemet stöds inte",
"unsupported_keyring_use_basic_text": "Använd svagare kryptering",
"unsupported_keyring_use_plaintext": "Använd ingen kryptering"
}
},
"view_menu": {
"actual_size": "Verklig storlek",
"toggle_developer_tools": "Växla utvecklarverktyg",

View File

@@ -32,20 +32,9 @@
"speech_start_speaking": "Почати говорити",
"speech_stop_speaking": "Припинити говорити"
},
"eol": {
"title": "Система не підтримується"
},
"file_menu": {
"label": "Файл"
},
"icon_overlay": {
"description_error": "Помилка",
"description_notifications": {
"one": "У вас є %(count)s непрочитане сповіщення.",
"few": "У вас є %(count)s непрочитані сповіщення.",
"many": "У вас є %(count)s непрочитаних сповіщень."
}
},
"menu": {
"hide": "Сховати",
"hide_others": "Сховати інші",
@@ -67,14 +56,13 @@
"backend_changed": "Очистити дані та перезавантажити?",
"backend_changed_detail": "Не вдається отримати доступ до таємного ключа з системного набору ключів, видається, він змінився.",
"backend_changed_title": "Не вдалося завантажити базу даних",
"backend_no_encryption": "Ваша система підтримує сховище ключів, але шифрування недоступне.",
"backend_no_encryption_detail": "Electron виявив, що шифрування недоступне у вашому сховищі ключів %(backend)s. Переконайтеся, що у вас встановлено сховище ключів. Якщо так, тоді перезавантажте комп'ютер і повторіть спробу. За бажанням, ви можете дозволити %(brand)s використовувати слабшу форму шифрування.",
"backend_no_encryption_title": "Шифрування не підтримується",
"unknown_backend_override": "Ваша система має непідтримуваний ключ, що означає, що базу даних неможливо відкрити.",
"unknown_backend_override_details": "Перегляньте журнал, щоб дізнатися подробиці.",
"unknown_backend_override_title": "Не вдалося завантажити базу даних",
"unsupported_keyring": "Ваша система має непідтримуваний набір ключів. Це означає, що базу даних неможливо відкрити.",
"unsupported_keyring_cta": "Використовувати слабше шифрування",
"unsupported_keyring_detail": "Electron не виявив підтримуваного бекенда для роботи зі сховищем паролів. Ви можете вручну налаштувати його, запустивши %(brand)s з відповідним аргументом у командному рядку. Цю дію потрібно виконати лише один раз. Докладніше %(link)s.",
"unsupported_keyring_title": "Система не підтримується",
"unsupported_keyring_use_basic_text": "Використовувати слабше шифрування",
"unsupported_keyring_use_plaintext": "Не використовувати шифрування"
"unsupported_keyring_title": "Система не підтримується"
}
},
"view_menu": {

View File

@@ -1,5 +1,5 @@
/*
Copyright 2022-2025 New Vector Ltd.
Copyright 2022-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
@@ -9,9 +9,22 @@ import { app, autoUpdater, desktopCapturer, ipcMain, powerSaveBlocker, TouchBar,
import IpcMainEvent = Electron.IpcMainEvent;
import { randomArray } from "./utils.js";
import { Settings } from "./settings.js";
import { getDisplayMediaCallback, setDisplayMediaCallback } from "./displayMediaCallback.js";
import Store, { clearDataAndRelaunch } from "./store.js";
ipcMain.on("setBadgeCount", function (_ev: IpcMainEvent, count: number): void {
if (process.platform !== "win32") {
// only set badgeCount on Mac/Linux, the docs say that only those platforms support it but turns out Electron
// has some Windows support too, and in some Windows environments this leads to two badges rendering atop
// each other. See https://github.com/vector-im/element-web/issues/16942
app.badgeCount = count;
}
if (count === 0) {
global.mainWindow?.flashFrame(false);
}
});
let focusHandlerAttached = false;
ipcMain.on("loudNotification", function (): void {
if (process.platform === "win32" || process.platform === "linux") {
@@ -56,6 +69,18 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
case "getUpdateFeedUrl":
ret = autoUpdater.getFeedURL();
break;
case "getSettingValue": {
const [settingName] = args;
const setting = Settings[settingName];
ret = await setting.read();
break;
}
case "setSettingValue": {
const [settingName, value] = args;
const setting = Settings[settingName];
await setting.write(value);
break;
}
case "setLanguage":
global.appLocalization.setAppLocale(args[0]);
break;
@@ -111,7 +136,7 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
try {
ret = await store.getSecret(`${args[0]}|${args[1]}`);
} catch {
// if an error is thrown (e.g. we can't initialise safeStorage),
// if an error is thrown (e.g. keytar can't connect to the keychain),
// then return null, which means the default pickle key will be used
ret = null;
}
@@ -149,7 +174,7 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
break;
case "clearStorage":
await clearDataAndRelaunch(global.mainWindow.webContents.session);
await clearDataAndRelaunch();
return; // the app is about to stop, we don't need to reply to the IPC
case "breadcrumbs": {
@@ -218,10 +243,3 @@ ipcMain.on("ipcCall", async function (_ev: IpcMainEvent, payload) {
});
ipcMain.handle("getConfig", () => global.vectorConfig);
const initialisePromiseWithResolvers = Promise.withResolvers<void>();
export const initialisePromise = initialisePromiseWithResolvers.promise;
ipcMain.once("initialise", () => {
initialisePromiseWithResolvers.resolve();
});

View File

@@ -52,10 +52,8 @@ export function setupMacosTitleBar(window: BrowserWindow): void {
-webkit-app-region: drag;
}
/* Exclude the main content elements from being drag handles */
.mx_AuthPage .mx_AuthPage_modalContent,
.mx_AuthPage .mx_AuthPage_modalBlur,
.mx_AuthPage .mx_AuthFooter > *,
.mx_AuthPage .mx_Dropdown_menu {
.mx_AuthPage .mx_AuthFooter > * {
-webkit-app-region: no-drag;
}
@@ -119,12 +117,6 @@ export function setupMacosTitleBar(window: BrowserWindow): void {
height: 20px;
-webkit-app-region: drag;
}
.mx_LeftPanel_newRoomList::before {
/* Aligned with the room header */
height: 13px;
border-right: 1px solid var(--cpd-color-bg-subtle-primary);
}
.mx_RoomView::before,
.mx_SpaceRoomView::before {

View File

@@ -32,7 +32,6 @@ const CHANNELS = [
"userAccessToken",
"homeserverUrl",
"serverSupportedVersions",
"showToast",
];
contextBridge.exposeInMainWorld("electron", {
@@ -55,25 +54,11 @@ contextBridge.exposeInMainWorld("electron", {
protocol: string;
sessionId: string;
config: IConfigOptions;
supportedSettings: Record<string, boolean>;
/**
* Do we need to render badge overlays for new notifications?
*/
supportsBadgeOverlay: boolean;
}> {
ipcRenderer.emit("initialise");
const [{ protocol, sessionId }, config, supportedSettings] = await Promise.all([
const [{ protocol, sessionId }, config] = await Promise.all([
ipcRenderer.invoke("getProtocol"),
ipcRenderer.invoke("getConfig"),
ipcRenderer.invoke("getSupportedSettings"),
]);
return { protocol, sessionId, config, supportedSettings, supportsBadgeOverlay: process.platform === "win32" };
},
async setSettingValue(settingName: string, value: any): Promise<void> {
return ipcRenderer.invoke("setSettingValue", settingName, value);
},
async getSettingValue(settingName: string): Promise<any> {
return ipcRenderer.invoke("getSettingValue", settingName);
return { protocol, sessionId, config };
},
});

View File

@@ -121,7 +121,7 @@ export default class ProtocolHandler {
public getProfileFromDeeplink(args: string[]): string | undefined {
// check if we are passed a profile in the SSO callback url
const deeplinkUrl = args.find(
(arg) => arg.startsWith(`${this.protocol}:/`) || arg.startsWith(`${LEGACY_PROTOCOL}://`),
(arg) => arg.startsWith(`${this.protocol}://`) || arg.startsWith(`${LEGACY_PROTOCOL}://`),
);
if (deeplinkUrl?.includes(SEARCH_PARAM)) {
const parsedUrl = new URL(deeplinkUrl);

View File

@@ -43,7 +43,7 @@ const seshatDefaultPassphrase = "DEFAULT_PASSPHRASE";
async function getOrCreatePassphrase(store: Store, key: string): Promise<string> {
try {
const storedPassphrase = await store.getSecret(key);
if (storedPassphrase !== undefined) {
if (storedPassphrase !== null) {
return storedPassphrase;
}
} catch (e) {

View File

@@ -5,25 +5,25 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { ipcMain } from "electron";
import * as tray from "./tray.js";
import Store from "./store.js";
import { AutoLaunch, type AutoLaunchState } from "./auto-launch.js";
interface Setting {
read(): Promise<any>;
write(value: any): Promise<void>;
supported?(): boolean; // if undefined, the setting is always supported
}
const Settings: Record<string, Setting> = {
export const Settings: Record<string, Setting> = {
"Electron.autoLaunch": {
async read(): Promise<AutoLaunchState> {
return AutoLaunch.instance.getState();
async read(): Promise<any> {
return global.launcher.isEnabled();
},
async write(value: AutoLaunchState): Promise<void> {
return AutoLaunch.instance.setState(value);
async write(value: any): Promise<void> {
if (value) {
return global.launcher.enable();
} else {
return global.launcher.disable();
}
},
},
"Electron.warnBeforeExit": {
@@ -35,10 +35,7 @@ const Settings: Record<string, Setting> = {
},
},
"Electron.alwaysShowMenuBar": {
// This isn't relevant on Mac as Menu bars don't live in the app window
supported(): boolean {
return process.platform !== "darwin";
},
// not supported on macOS
async read(): Promise<any> {
return !global.mainWindow!.autoHideMenuBar;
},
@@ -49,10 +46,7 @@ const Settings: Record<string, Setting> = {
},
},
"Electron.showTrayIcon": {
// Things other than Mac support tray icons
supported(): boolean {
return process.platform !== "darwin";
},
// not supported on macOS
async read(): Promise<any> {
return tray.hasTray();
},
@@ -74,43 +68,4 @@ const Settings: Record<string, Setting> = {
Store.instance?.set("disableHardwareAcceleration", !value);
},
},
"Electron.enableContentProtection": {
// Unsupported on Linux https://www.electronjs.org/docs/latest/api/browser-window#winsetcontentprotectionenable-macos-windows
// Broken on macOS https://github.com/electron/electron/issues/19880
supported(): boolean {
return process.platform === "win32";
},
async read(): Promise<any> {
return Store.instance?.get("enableContentProtection");
},
async write(value: any): Promise<void> {
global.mainWindow?.setContentProtection(value);
Store.instance?.set("enableContentProtection", value);
},
},
};
ipcMain.handle("getSupportedSettings", async () => {
const supportedSettings: Record<string, boolean> = {};
for (const [key, setting] of Object.entries(Settings)) {
supportedSettings[key] = setting.supported?.() ?? true;
}
return supportedSettings;
});
ipcMain.handle("setSettingValue", async (_ev, settingName: string, value: any) => {
const setting = Settings[settingName];
if (!setting) {
throw new Error(`Unknown setting: ${settingName}`);
}
console.debug(`Writing setting value for: ${settingName} = ${value}`);
await setting.write(value);
});
ipcMain.handle("getSettingValue", async (_ev, settingName: string) => {
const setting = Settings[settingName];
if (!setting) {
throw new Error(`Unknown setting: ${settingName}`);
}
const value = await setting.read();
console.debug(`Reading setting value for: ${settingName} = ${value}`);
return value;
});

View File

@@ -15,10 +15,23 @@ limitations under the License.
*/
import ElectronStore from "electron-store";
import { app, safeStorage, dialog, type SafeStorage, type Session } from "electron";
import keytar from "keytar-forked";
import { app, safeStorage, dialog, type SafeStorage } from "electron";
import { clearAllUserData, relaunchApp } from "@standardnotes/electron-clear-data";
import { _t } from "./language-helper.js";
/**
* Legacy keytar service name for storing secrets.
* @deprecated
*/
const KEYTAR_SERVICE = "element.io";
/**
* Super legacy keytar service name for reading secrets.
* @deprecated
*/
const LEGACY_KEYTAR_SERVICE = "riot.im";
/**
* String union type representing all the safeStorage backends.
* + The "unknown" backend shouldn't exist in practice once the app is ready
@@ -28,37 +41,28 @@ import { _t } from "./language-helper.js";
* + All other backends are linux-specific and are encrypted using the keychain
*/
type SafeStorageBackend = ReturnType<SafeStorage["getSelectedStorageBackend"]> | "system" | "plaintext";
/**
* The "unknown" backend is not a valid backend, so we exclude it from the type.
*/
type SaneSafeStorageBackend = Exclude<SafeStorageBackend, "unknown">;
/**
* Map of safeStorage backends to their command line arguments.
* kwallet6 cannot be specified via command line
* https://www.electronjs.org/docs/latest/api/safe-storage#safestoragegetselectedstoragebackend-linux
*/
const safeStorageBackendMap: Omit<Record<SaneSafeStorageBackend, string>, "system" | "plaintext"> = {
const safeStorageBackendMap: Omit<
Record<SafeStorageBackend, string>,
"unknown" | "kwallet6" | "system" | "plaintext"
> = {
basic_text: "basic",
gnome_libsecret: "gnome-libsecret",
kwallet: "kwallet",
kwallet5: "kwallet5",
kwallet6: "kwallet6",
};
function relaunchApp(): void {
console.info("Relaunching app...");
app.relaunch();
app.exit();
}
/**
* Clear all data and relaunch the app.
*/
export async function clearDataAndRelaunch(electronSession: Session): Promise<void> {
export async function clearDataAndRelaunch(): Promise<void> {
Store.instance?.clear();
electronSession.flushStorageData();
await electronSession.clearStorageData();
clearAllUserData();
relaunchApp();
}
@@ -69,7 +73,6 @@ interface StoreData {
autoHideMenuBar: boolean;
locale?: string | string[];
disableHardwareAcceleration: boolean;
enableContentProtection: boolean;
safeStorage?: Record<string, string>;
/** the safeStorage backend used for the safeStorage data as written */
safeStorageBackend?: SafeStorageBackend;
@@ -77,14 +80,12 @@ interface StoreData {
safeStorageBackendOverride?: boolean;
/** whether to perform a migration of the safeStorage data */
safeStorageBackendMigrate?: boolean;
/** whether to open the app at login minimised, only valid when app.openAtLogin is true */
openAtLoginMinimised: boolean;
}
/**
* Fallback storage writer for secrets, mainly used for automated tests and systems without any safeStorage support.
*/
class StorageWriter {
class PlaintextStorageWriter {
public constructor(protected readonly store: ElectronStore<StoreData>) {}
public getKey(key: string): `safeStorage.${string}` {
@@ -95,7 +96,7 @@ class StorageWriter {
this.store.set(this.getKey(key), secret);
}
public get(key: string): string | undefined {
public get(key: string): string | null {
return this.store.get(this.getKey(key));
}
@@ -107,12 +108,12 @@ class StorageWriter {
/**
* Storage writer for secrets using safeStorage.
*/
class SafeStorageWriter extends StorageWriter {
class SafeStorageWriter extends PlaintextStorageWriter {
public set(key: string, secret: string): void {
this.store.set(this.getKey(key), safeStorage.encryptString(secret).toString("base64"));
}
public get(key: string): string | undefined {
public get(key: string): string | null {
const ciphertext = this.store.get<string, string | undefined>(this.getKey(key));
if (ciphertext) {
try {
@@ -122,7 +123,7 @@ class SafeStorageWriter extends StorageWriter {
console.error("...ciphertext:", JSON.stringify(ciphertext));
}
}
return undefined;
return null;
}
}
@@ -135,7 +136,7 @@ const enum Mode {
/**
* JSON-backed store for settings which need to be accessible by the main process.
* Secrets are stored within the `safeStorage` object, encrypted with safeStorage.
* Any secrets operations are blocked on Electron app ready emit.
* Any secrets operations are blocked on Electron app ready emit, and keytar migration if still needed.
*/
class Store extends ElectronStore<StoreData> {
private static internalInstance?: Store;
@@ -156,10 +157,7 @@ class Store extends ElectronStore<StoreData> {
const store = new Store(mode ?? Mode.Encrypted);
Store.internalInstance = store;
if (
process.platform === "linux" &&
(store.get("safeStorageBackendOverride") || store.get("safeStorageBackendMigrate"))
) {
if (process.platform === "linux" && store.get("safeStorageBackendOverride")) {
const backend = store.get("safeStorageBackend")!;
if (backend in safeStorageBackendMap) {
// If the safeStorage backend which was used to write the data is one we can specify via the commandLine
@@ -176,7 +174,7 @@ class Store extends ElectronStore<StoreData> {
// Provides "raw" access to the underlying secrets storage,
// should be avoided in favour of the getSecret/setSecret/deleteSecret methods.
private secrets?: StorageWriter;
private secrets?: PlaintextStorageWriter | SafeStorageWriter;
private constructor(private mode: Mode) {
super({
@@ -206,10 +204,6 @@ class Store extends ElectronStore<StoreData> {
type: "boolean",
default: false,
},
enableContentProtection: {
type: "boolean",
default: false,
},
safeStorage: {
type: "object",
},
@@ -222,185 +216,160 @@ class Store extends ElectronStore<StoreData> {
safeStorageBackendMigrate: {
type: "boolean",
},
openAtLoginMinimised: {
type: "boolean",
default: true,
},
},
});
}
private safeStorageReadyPromise?: Promise<boolean>;
private safeStorageReadyPromise?: Promise<unknown>;
public async safeStorageReady(): Promise<void> {
if (!this.safeStorageReadyPromise) {
throw new Error("prepareSafeStorage must be called before using storage methods");
this.safeStorageReadyPromise = this.prepareSafeStorage();
}
await this.safeStorageReadyPromise;
}
/**
* Normalise the backend to a sane value (exclude `unknown`), respect forcePlaintext mode,
* and ensure that if an encrypted backend is picked that encryption is available, falling back to plaintext if not.
* @param forcePlaintext - whether to force plaintext mode
* @private
* Prepare the safeStorage backend for use.
* We don't eagerly import from keytar as that would bring in data for all Element profiles and not just the current one,
* so we import lazily in getSecret.
*/
private chooseBackend(forcePlaintext: boolean): SaneSafeStorageBackend {
if (forcePlaintext) {
return "plaintext";
}
private async prepareSafeStorage(): Promise<void> {
await app.whenReady();
let safeStorageBackend = this.get("safeStorageBackend");
if (process.platform === "linux") {
// Linux safeStorage support is hellish, the support varies on the Desktop Environment used rather than the store itself.
// https://github.com/electron/electron/issues/39789 https://github.com/microsoft/vscode/issues/185212
const selectedSafeStorageBackend = safeStorage.getSelectedStorageBackend();
// The following enables plain text encryption if the backend used is basic_text.
// It has no significance for any other backend.
// We do this early so that in case we end up using the basic_text backend (either because that's the only one available
// or as a fallback when the configured backend lacks encryption support), encryption is already turned on.
safeStorage.setUsePlainTextEncryption(true);
// Linux safeStorage support is hellish, the support varies on the Desktop Environment used rather than the store itself.
// https://github.com/electron/electron/issues/39789 https://github.com/microsoft/vscode/issues/185212
const selectedBackend = safeStorage.getSelectedStorageBackend();
const isEncryptionAvailable = safeStorage.isEncryptionAvailable();
console.info(
`safeStorage backend '${selectedSafeStorageBackend}' selected, '${safeStorageBackend}' in config, isEncryptionAvailable = ${isEncryptionAvailable}.`,
);
if (selectedBackend === "unknown" || !safeStorage.isEncryptionAvailable()) {
return "plaintext";
}
return selectedBackend;
}
return safeStorage.isEncryptionAvailable() ? "system" : "plaintext";
}
/**
* Prepare the safeStorage backend for use.
*
* This will relaunch the app in some cases, in which case it will return false and the caller should abort startup.
*
* @param electronSession - The Electron session to use for storage (will be used to clear storage if necessary).
* @returns true if safeStorage was initialised successfully or false if the app will be relaunched
*/
public async prepareSafeStorage(electronSession: Session): Promise<boolean> {
this.safeStorageReadyPromise = this.reallyPrepareSafeStorage(electronSession);
return this.safeStorageReadyPromise;
}
private async reallyPrepareSafeStorage(electronSession: Session): Promise<boolean> {
await app.whenReady();
// The backend the existing data is written with if any
let existingSafeStorageBackend = this.get("safeStorageBackend");
// The backend and encryption status of the currently loaded backend
const backend = this.chooseBackend(this.mode === Mode.ForcePlaintext);
// Handle migrations
if (existingSafeStorageBackend) {
if (existingSafeStorageBackend === "basic_text" && backend !== "plaintext" && backend !== "basic_text") {
this.prepareMigrateBasicTextToPlaintext();
return false;
if (selectedSafeStorageBackend === "unknown") {
// This should never happen but good to be safe
await dialog.showMessageBox({
title: _t("store|error|unknown_backend_override_title"),
message: _t("store|error|unknown_backend_override"),
detail: _t("store|error|unknown_backend_override_details"),
type: "error",
});
throw new Error("safeStorage backend unknown");
}
if (this.get("safeStorageBackendMigrate") && backend === "basic_text") {
this.migrateBasicTextToPlaintext();
return false;
if (this.get("safeStorageBackendMigrate")) {
return this.upgradeLinuxBackend2();
}
if (existingSafeStorageBackend === "plaintext" && backend !== "plaintext") {
this.migratePlaintextToEncrypted();
// Ensure we update existingSafeStorageBackend so we don't fall into the "backend changed" clause below
existingSafeStorageBackend = this.get("safeStorageBackend");
}
}
// Whether we were using basic_text as a fallback before
const usingFallback = this.get("safeStorageBackendOverride") && safeStorageBackend === "basic_text";
if (!existingSafeStorageBackend) {
// First launch of the app or first launch since the update
if (this.mode === Mode.Encrypted && (backend === "plaintext" || backend === "basic_text")) {
// Ask the user for consent to use a degraded mode
await this.consultUserConsentDegradedMode(backend);
}
// Store the backend used for the safeStorage data so we can detect if it changes, and we know how the data is encoded
this.recordSafeStorageBackend(backend);
} else if (existingSafeStorageBackend !== backend) {
// We already appear to have started using a backend other than the one that we picked, so
// set the override flag and relaunch with the backend we were previously using, unless we
// already have the override flag, in which case we must assume the previous backend is no
// longer usable, in which case we should fall into the next block and warn the user we can't
// migrate.
console.warn(`safeStorage backend changed from ${existingSafeStorageBackend} to ${backend}`);
if (existingSafeStorageBackend in safeStorageBackendMap && !this.get("safeStorageBackendOverride")) {
if (this.mode === Mode.Encrypted && !isEncryptionAvailable && !usingFallback) {
// Sometimes we may have a working backend that for some reason does not support encryption at the moment.
// This may be because electron reported an incorrect backend or because of some known issues with the keyring itself.
// In any case, when this happens, we give the user an option to use a weaker form of encryption.
const { response } = await dialog.showMessageBox({
title: _t("store|error|backend_no_encryption_title"),
message: _t("store|error|backend_no_encryption"),
detail: _t("store|error|backend_no_encryption_detail", {
backend: safeStorageBackend,
brand: global.vectorConfig.brand || "Element",
}),
type: "error",
buttons: [_t("action|cancel"), _t("store|error|unsupported_keyring_cta")],
defaultId: 0,
cancelId: 0,
});
if (response === 0) {
throw new Error(
`Encryption support not available on backend ${safeStorageBackend} and user prohibits using weaker encryption.`,
);
}
this.recordSafeStorageBackend("basic_text");
this.set("safeStorageBackendOverride", true);
relaunchApp();
return false;
} else {
// This will either relaunch the app or throw an execption
await this.consultUserBackendChangedUnableToMigrate(electronSession);
return false;
} else if (usingFallback) {
// On the next run, don't use the fallback.
// This is so that we can check if the problems with the keyring fixed itself.
this.set("safeStorageBackendOverride", false);
} else if (!safeStorageBackend) {
if (selectedSafeStorageBackend === "basic_text" && this.mode === Mode.Encrypted) {
const { response } = await dialog.showMessageBox({
title: _t("store|error|unsupported_keyring_title"),
message: _t("store|error|unsupported_keyring"),
detail: _t("store|error|unsupported_keyring_detail", {
brand: global.vectorConfig.brand || "Element",
link: "https://www.electronjs.org/docs/latest/api/safe-storage#safestoragegetselectedstoragebackend-linux",
}),
type: "error",
buttons: [_t("action|cancel"), _t("store|error|unsupported_keyring_cta")],
defaultId: 0,
cancelId: 0,
});
if (response === 0) {
throw new Error("safeStorage backend basic_text and user rejected it");
}
this.mode = Mode.AllowPlaintext;
}
// Store the backend used for the safeStorage data so we can detect if it changes
this.recordSafeStorageBackend(selectedSafeStorageBackend);
safeStorageBackend = selectedSafeStorageBackend;
} else if (safeStorageBackend !== selectedSafeStorageBackend) {
console.warn(`safeStorage backend changed from ${safeStorageBackend} to ${selectedSafeStorageBackend}`);
if (safeStorageBackend === "basic_text") {
return this.upgradeLinuxBackend1();
} else if (safeStorageBackend === "plaintext") {
this.upgradeLinuxBackend3();
} else if (safeStorageBackend in safeStorageBackendMap) {
this.set("safeStorageBackendOverride", true);
relaunchApp();
return;
} else {
// Warn the user that the backend has changed and tell them that we cannot migrate
const { response } = await dialog.showMessageBox({
title: _t("store|error|backend_changed_title"),
message: _t("store|error|backend_changed"),
detail: _t("store|error|backend_changed_detail"),
type: "question",
buttons: [_t("common|no"), _t("common|yes")],
defaultId: 0,
cancelId: 0,
});
if (response === 0) {
throw new Error("safeStorage backend changed and cannot migrate");
}
await clearDataAndRelaunch();
}
}
// We do not check allowPlaintextStorage here as it was already checked above if the storage is new
// and if the storage is existing then we should continue to honour the backend used to write the data
if (safeStorageBackend === "basic_text" && selectedSafeStorageBackend === safeStorageBackend) {
safeStorage.setUsePlainTextEncryption(true);
}
} else if (!safeStorageBackend) {
safeStorageBackend = this.mode === Mode.Encrypted ? "system" : "plaintext";
this.recordSafeStorageBackend(safeStorageBackend);
}
console.info(`Using storage mode '${this.mode}' with backend '${backend}'`);
if (backend !== "plaintext") {
if (this.mode !== Mode.ForcePlaintext && safeStorage.isEncryptionAvailable()) {
this.secrets = new SafeStorageWriter(this);
} else if (this.mode !== Mode.Encrypted) {
this.secrets = new PlaintextStorageWriter(this);
} else {
this.secrets = new StorageWriter(this);
throw new Error(`safeStorage is not available`);
}
return true;
}
private async consultUserBackendChangedUnableToMigrate(electronSession: Session): Promise<void> {
const { response } = await dialog.showMessageBox({
title: _t("store|error|backend_changed_title"),
message: _t("store|error|backend_changed"),
detail: _t("store|error|backend_changed_detail"),
type: "question",
buttons: [_t("common|no"), _t("common|yes")],
defaultId: 0,
cancelId: 0,
});
if (response === 0) {
throw new Error("safeStorage backend changed and cannot migrate");
}
return clearDataAndRelaunch(electronSession);
}
private async consultUserConsentDegradedMode(backend: "plaintext" | "basic_text"): Promise<void> {
if (backend === "plaintext") {
// Sometimes we may have a working backend that for some reason does not support encryption at the moment.
// This may be because electron reported an incorrect backend or because of some known issues with the keyring itself.
// Or the environment specified `--storage-mode=force-plaintext`.
// In any case, when this happens, we give the user an option to use a weaker form of encryption.
const { response } = await dialog.showMessageBox({
title: _t("store|error|backend_no_encryption_title"),
message: _t("store|error|backend_no_encryption"),
detail: _t("store|error|backend_no_encryption_detail", {
backend: safeStorage.getSelectedStorageBackend(),
brand: global.vectorConfig.brand || "Element",
}),
type: "error",
buttons: [_t("action|cancel"), _t("store|error|unsupported_keyring_use_plaintext")],
defaultId: 0,
cancelId: 0,
});
if (response === 0) {
throw new Error("isEncryptionAvailable=false and user rejected plaintext");
}
} else {
// Electron did not identify a compatible encrypted backend, ask user for consent to degraded mode
const { response } = await dialog.showMessageBox({
title: _t("store|error|unsupported_keyring_title"),
message: _t("store|error|unsupported_keyring"),
detail: _t("store|error|unsupported_keyring_detail", {
brand: global.vectorConfig.brand || "Element",
link: "https://www.electronjs.org/docs/latest/api/safe-storage#safestoragegetselectedstoragebackend-linux",
}),
type: "error",
buttons: [_t("action|cancel"), _t("store|error|unsupported_keyring_use_basic_text")],
defaultId: 0,
cancelId: 0,
});
if (response === 0) {
throw new Error("safeStorage backend basic_text and user rejected it");
}
}
console.info(`Using storage mode '${this.mode}' with backend '${safeStorageBackend}'`);
}
private recordSafeStorageBackend(backend: SafeStorageBackend): void {
@@ -410,37 +379,35 @@ class Store extends ElectronStore<StoreData> {
/**
* Linux support for upgrading the backend from basic_text to one of the encrypted backends,
* this is quite a tricky process as the backend is not known until the app is ready & cannot be changed once it is.
* 1. We restart the app in safeStorageBackendMigrate mode
* 2. Now that we are in the mode which our data is written in we decrypt the data, write it back in plaintext
* & restart back in default backend mode,
* 3. Finally, we load the plaintext data & encrypt it.
* First we restart the app in basic_text backend mode, then decrypt the data & restart back in default backend mode,
* and re-encrypt the data.
*/
private prepareMigrateBasicTextToPlaintext(): void {
private upgradeLinuxBackend1(): void {
console.info(`Starting safeStorage migration to ${safeStorage.getSelectedStorageBackend()}`);
this.set("safeStorageBackendMigrate", true);
relaunchApp();
}
private migrateBasicTextToPlaintext(): void {
const secrets = new SafeStorageWriter(this);
private upgradeLinuxBackend2(): void {
this.secrets = new PlaintextStorageWriter(this);
console.info("Performing safeStorage migration");
const data = this.get("safeStorage");
if (data) {
for (const key in data) {
this.set(secrets.getKey(key), secrets.get(key));
this.set(this.secrets.getKey(key), this.secrets!.get(key));
}
this.recordSafeStorageBackend("plaintext");
}
this.delete("safeStorageBackendMigrate");
this.set("safeStorageBackendMigrate", false);
relaunchApp();
}
private migratePlaintextToEncrypted(): void {
const secrets = new SafeStorageWriter(this);
private upgradeLinuxBackend3(): void {
this.secrets = new PlaintextStorageWriter(this);
const selectedSafeStorageBackend = safeStorage.getSelectedStorageBackend();
console.info(`Finishing safeStorage migration to ${selectedSafeStorageBackend}`);
const data = this.get("safeStorage");
if (data) {
for (const key in data) {
secrets.set(key, data[key]);
this.secrets.set(key, data[key]);
}
}
this.recordSafeStorageBackend(selectedSafeStorageBackend);
@@ -448,18 +415,33 @@ class Store extends ElectronStore<StoreData> {
/**
* Get the stored secret for the key.
* Lazily migrates keys from keytar if they are not yet in the store.
*
* @param key The string key name.
*
* @returns A promise for the secret string.
*/
public async getSecret(key: string): Promise<string | undefined> {
public async getSecret(key: string): Promise<string | null> {
await this.safeStorageReady();
return this.secrets!.get(key);
let secret = this.secrets!.get(key);
if (secret) return secret;
try {
secret = await this.getSecretKeytar(key);
} catch (e) {
console.warn(`Failed to read data from keytar with key='${key}'`, e);
}
if (secret) {
console.debug("Migrating secret from keytar", key);
this.secrets!.set(key, secret);
}
return secret;
}
/**
* Add the secret for the key to the keychain.
* We write to both safeStorage & keytar to support downgrading the application.
*
* @param key The string key name.
* @param secret The string password.
@@ -469,16 +451,44 @@ class Store extends ElectronStore<StoreData> {
public async setSecret(key: string, secret: string): Promise<void> {
await this.safeStorageReady();
this.secrets!.set(key, secret);
try {
await keytar.setPassword(KEYTAR_SERVICE, key, secret);
} catch (e) {
console.warn(`Failed to write safeStorage backwards-compatibility key='${key}' data to keytar`, e);
}
}
/**
* Delete the stored password for the key.
* Removes from safeStorage, keytar & keytar legacy.
*
* @param key The string key name.
*/
public async deleteSecret(key: string): Promise<void> {
await this.safeStorageReady();
this.secrets!.delete(key);
try {
await this.deleteSecretKeytar(key);
} catch (e) {
console.warn(`Failed to delete secret with key='${key}' from keytar`, e);
}
}
/**
* @deprecated will be removed in the near future
*/
private async getSecretKeytar(key: string): Promise<string | null> {
return (
(await keytar.getPassword(KEYTAR_SERVICE, key)) ?? (await keytar.getPassword(LEGACY_KEYTAR_SERVICE, key))
);
}
/**
* @deprecated will be removed in the near future
*/
private async deleteSecretKeytar(key: string): Promise<void> {
await keytar.deletePassword(LEGACY_KEYTAR_SERVICE, key);
await keytar.deletePassword(KEYTAR_SERVICE, key);
}
}

View File

@@ -1,5 +1,5 @@
/*
Copyright 2024-2025 New Vector Ltd.
Copyright 2024 New Vector Ltd.
Copyright 2017 Karl Glatz <karl@glatz.biz>
Copyright 2017 OpenMarket Ltd
@@ -8,10 +8,10 @@ Please see LICENSE files in the repository root for full details.
*/
import { app, Tray, Menu, nativeImage } from "electron";
import { v5 as uuidv5 } from "uuid";
import { writeFile } from "node:fs/promises";
import pngToIco from "png-to-ico";
import path from "node:path";
import fs from "node:fs";
import { v5 as uuidv5 } from "uuid";
import { _t } from "./language-helper.js";
@@ -38,13 +38,18 @@ function toggleWin(): void {
}
}
interface IConfig {
icon_path: string; // eslint-disable-line camelcase
brand: string;
}
function getUuid(): string {
// The uuid field is optional and only needed on unsigned Windows packages where the executable path changes
// The hardcoded uuid is an arbitrary v4 uuid generated on https://www.uuidgenerator.net/version4
return global.vectorConfig["uuid"] || "eba84003-e499-4563-8e9d-166e34b5cc25";
}
export function create(config: (typeof global)["trayConfig"]): void {
export function create(config: IConfig): void {
// no trays on darwin
if (process.platform === "darwin" || trayIcon) return;
const defaultIcon = nativeImage.createFromPath(config.icon_path);
@@ -66,7 +71,6 @@ export function create(config: (typeof global)["trayConfig"]): void {
initApplicationMenu();
trayIcon.on("click", toggleWin);
// See also, badge.ts
let lastFavicon: string | null = null;
global.mainWindow?.webContents.on("page-favicon-updated", async function (ev, favicons) {
if (!favicons || favicons.length <= 0 || !favicons[0].startsWith("data:")) {
@@ -88,17 +92,15 @@ export function create(config: (typeof global)["trayConfig"]): void {
if (process.platform === "win32") {
try {
const icoPath = path.join(app.getPath("temp"), "win32_element_icon.ico");
await writeFile(icoPath, await pngToIco(newFavicon.toPNG()));
fs.writeFileSync(icoPath, await pngToIco(newFavicon.toPNG()));
newFavicon = nativeImage.createFromPath(icoPath);
} catch (e) {
console.error("Failed to make win32 ico", e);
}
// Always update the tray icon for Windows.
trayIcon?.setImage(newFavicon);
} else {
trayIcon?.setImage(newFavicon);
global.mainWindow?.setIcon(newFavicon);
}
trayIcon?.setImage(newFavicon);
global.mainWindow?.setIcon(newFavicon);
});
global.mainWindow?.webContents.on("page-title-updated", function (ev, title) {

View File

@@ -7,11 +7,8 @@ Please see LICENSE files in the repository root for full details.
import { app, autoUpdater, ipcMain } from "electron";
import fs from "node:fs/promises";
import os from "node:os";
import { getSquirrelExecutable } from "./squirrelhooks.js";
import { _t } from "./language-helper.js";
import { initialisePromise } from "./ipc.js";
const UPDATE_POLL_INTERVAL_MS = 60 * 60 * 1000;
const INITIAL_UPDATE_DELAY_MS = 30 * 1000;
@@ -72,8 +69,7 @@ async function pollForUpdates(): Promise<void> {
}
export async function start(updateBaseUrl: string): Promise<void> {
if (!(await available())) return;
console.log(`Starting auto update with base URL: ${updateBaseUrl}`);
if (!(await available(updateBaseUrl))) return;
if (!updateBaseUrl.endsWith("/")) {
updateBaseUrl = updateBaseUrl + "/";
}
@@ -115,15 +111,10 @@ export async function start(updateBaseUrl: string): Promise<void> {
}
}
/**
* Check if auto update is available on this platform.
* Has a side effect of firing showToast on EOL platforms so must only be called once!
* @returns True if auto update is available
*/
async function available(): Promise<boolean> {
async function available(updateBaseUrl?: string): Promise<boolean> {
if (process.platform === "linux") {
// Auto update is not supported on Linux
console.warn("Auto update not supported on this platform");
console.log("Auto update not supported on this platform");
return false;
}
@@ -131,42 +122,13 @@ async function available(): Promise<boolean> {
try {
await fs.access(getSquirrelExecutable());
} catch {
console.warn("Squirrel not found, auto update not supported");
console.log("Squirrel not found, auto update not supported");
return false;
}
}
// Otherwise we're either on macOS or Windows with Squirrel
if (process.platform === "darwin") {
// OS release returns the Darwin kernel version, not the macOS version, see
// https://en.wikipedia.org/wiki/Darwin_(operating_system)#Release_history to interpret it
const release = os.release();
const major = parseInt(release.split(".")[0], 10);
if (major < 21) {
// If the macOS version is too old for modern Electron support then disable auto update to prevent the app updating and bricking itself.
// The oldest macOS version supported by Chromium/Electron 38 is Monterey (12.x) which started with Darwin 21.0
initialisePromise.then(() => {
ipcMain.emit("showToast", {
title: _t("eol|title"),
description: _t("eol|no_more_updates", { brand: global.trayConfig.brand }),
});
});
console.warn("Auto update not supported, macOS version too old");
return false;
} else if (major < 22) {
// If the macOS version is EOL then show a warning message.
// The oldest macOS version still supported by Apple is Ventura (13.x) which started with Darwin 22.0
initialisePromise.then(() => {
ipcMain.emit("showToast", {
title: _t("eol|title"),
description: _t("eol|warning", { brand: global.trayConfig.brand }),
});
});
}
}
return true;
return !!updateBaseUrl;
}
ipcMain.on("install_update", installUpdate);

View File

@@ -11,7 +11,7 @@
"rootDir": "./src",
"declaration": true,
"typeRoots": ["src/@types", "node_modules/@types"],
"lib": ["es2022", "es2024.promise"],
"lib": ["es2022"],
"types": ["node"],
"strict": true
},

5429
yarn.lock
View File

File diff suppressed because it is too large Load Diff