├── .dockerignore
├── prototypes
├── ie11.css
├── non-ie11.css
├── me.jpg
├── manifest.appcache
├── tools
│ └── package.json
├── ie11-hashchange.html
├── base256.html
├── online.html
└── ie11-textdecoder.html
├── scripts
├── sdk
│ ├── test
│ │ ├── .gitignore
│ │ ├── deps.d.ts
│ │ ├── package.json
│ │ ├── index.html
│ │ ├── test-sdk-in-esm-vite-build-env.js
│ │ ├── test-sdk-in-commonjs-env.js
│ │ └── esm-entry.ts
│ ├── create-manifest.js
│ ├── base-manifest.json
│ ├── build.sh
│ └── transform-paths.js
├── cleanup.sh
├── package-overrides
│ ├── safe-buffer
│ │ └── index.js
│ └── buffer
│ │ └── index.js
├── test-derived-theme
│ └── test-theme.sh
├── package.sh
├── .eslintrc.js
├── release.sh
├── test-app.sh
└── postcss
│ └── tests
│ └── common.js
├── doc
├── architecture
│ ├── UI
│ │ └── index.md
│ ├── images
│ │ ├── coloring-process.png
│ │ ├── svg-icon-example.png
│ │ └── theming-architecture.png
│ ├── persisted-network-calls.md
│ └── sync-updates.md
├── problem solving
│ ├── domexception_mapping.md
│ └── IMPORT-ISSUES.md
└── implementation planning
│ ├── html-messages.md
│ ├── READ-RECEIPTS.md
│ ├── timeline-member.md
│ ├── LOGIN.md
│ ├── DESIGN.md
│ ├── RELEASE.md
│ ├── session-container.md
│ ├── background-tasks.md
│ ├── CATCHUP-BACKFILL.md
│ ├── LOCAL-ECHO-STATE.md
│ ├── ROOM-VERSIONS.md
│ ├── PUSH.md
│ └── REPLIES.md
├── playwright
├── plugins
│ ├── synapsedocker
│ │ └── templates
│ │ │ ├── consent
│ │ │ ├── README.md
│ │ │ └── res
│ │ │ │ └── templates
│ │ │ │ └── privacy
│ │ │ │ └── en
│ │ │ │ ├── success.html
│ │ │ │ └── 1.0.html
│ │ │ ├── default
│ │ │ └── README.md
│ │ │ └── COPYME
│ │ │ └── README.md
│ └── dex
│ │ └── template
│ │ └── dev.db
├── tests
│ └── startup.spec.ts
└── global-setup.ts
├── src
├── platform
│ └── web
│ │ ├── assets
│ │ ├── icon.png
│ │ ├── icon-maskable.png
│ │ ├── config.json
│ │ ├── manifest.json
│ │ ├── icon.svg
│ │ └── icon-maskable.svg
│ │ ├── public
│ │ └── icon.png
│ │ ├── ui
│ │ ├── css
│ │ │ ├── themes
│ │ │ │ ├── element
│ │ │ │ │ ├── inter
│ │ │ │ │ │ ├── Inter-Black.woff
│ │ │ │ │ │ ├── Inter-Black.woff2
│ │ │ │ │ │ ├── Inter-Bold.woff
│ │ │ │ │ │ ├── Inter-Bold.woff2
│ │ │ │ │ │ ├── Inter-Italic.woff
│ │ │ │ │ │ ├── Inter-Light.woff
│ │ │ │ │ │ ├── Inter-Light.woff2
│ │ │ │ │ │ ├── Inter-Medium.woff
│ │ │ │ │ │ ├── Inter-Thin.woff
│ │ │ │ │ │ ├── Inter-Thin.woff2
│ │ │ │ │ │ ├── Inter-Italic.woff2
│ │ │ │ │ │ ├── Inter-Medium.woff2
│ │ │ │ │ │ ├── Inter-Regular.woff
│ │ │ │ │ │ ├── Inter-Regular.woff2
│ │ │ │ │ │ ├── Inter-SemiBold.woff
│ │ │ │ │ │ ├── Inter-BlackItalic.woff
│ │ │ │ │ │ ├── Inter-BoldItalic.woff
│ │ │ │ │ │ ├── Inter-BoldItalic.woff2
│ │ │ │ │ │ ├── Inter-ExtraBold.woff
│ │ │ │ │ │ ├── Inter-ExtraBold.woff2
│ │ │ │ │ │ ├── Inter-ExtraLight.woff
│ │ │ │ │ │ ├── Inter-ExtraLight.woff2
│ │ │ │ │ │ ├── Inter-LightItalic.woff
│ │ │ │ │ │ ├── Inter-SemiBold.woff2
│ │ │ │ │ │ ├── Inter-ThinItalic.woff
│ │ │ │ │ │ ├── Inter-ThinItalic.woff2
│ │ │ │ │ │ ├── Inter-BlackItalic.woff2
│ │ │ │ │ │ ├── Inter-LightItalic.woff2
│ │ │ │ │ │ ├── Inter-MediumItalic.woff
│ │ │ │ │ │ ├── Inter-MediumItalic.woff2
│ │ │ │ │ │ ├── Inter-ExtraBoldItalic.woff
│ │ │ │ │ │ ├── Inter-ExtraBoldItalic.woff2
│ │ │ │ │ │ ├── Inter-ExtraLightItalic.woff
│ │ │ │ │ │ ├── Inter-SemiBoldItalic.woff
│ │ │ │ │ │ ├── Inter-SemiBoldItalic.woff2
│ │ │ │ │ │ └── Inter-ExtraLightItalic.woff2
│ │ │ │ │ ├── icons
│ │ │ │ │ │ ├── e2ee-normal.svg
│ │ │ │ │ │ ├── chevron-right.svg
│ │ │ │ │ │ ├── chevron-thin-left.svg
│ │ │ │ │ │ ├── encryption-status.svg
│ │ │ │ │ │ ├── clear.svg
│ │ │ │ │ │ ├── dismiss.svg
│ │ │ │ │ │ ├── video-call.svg
│ │ │ │ │ │ ├── chevron-small.svg
│ │ │ │ │ │ ├── info.svg
│ │ │ │ │ │ ├── paperclip.svg
│ │ │ │ │ │ ├── cam-unmuted.svg
│ │ │ │ │ │ ├── send.svg
│ │ │ │ │ │ ├── disable-grid.svg
│ │ │ │ │ │ ├── verified.svg
│ │ │ │ │ │ ├── e2ee-disabled.svg
│ │ │ │ │ │ ├── plus.svg
│ │ │ │ │ │ ├── verification-error.svg
│ │ │ │ │ │ ├── vertical-ellipsis.svg
│ │ │ │ │ │ ├── chevron-left.svg
│ │ │ │ │ │ ├── search.svg
│ │ │ │ │ │ ├── cam-muted.svg
│ │ │ │ │ │ ├── hangup.svg
│ │ │ │ │ │ ├── voice-call.svg
│ │ │ │ │ │ ├── enable-grid.svg
│ │ │ │ │ │ ├── mic-unmuted.svg
│ │ │ │ │ │ └── chevron-down.svg
│ │ │ │ │ ├── error.css
│ │ │ │ │ ├── element-logo.svg
│ │ │ │ │ └── manifest.json
│ │ │ │ └── README.md
│ │ │ ├── popup.css
│ │ │ ├── status.css
│ │ │ ├── font.css
│ │ │ ├── form.css
│ │ │ ├── left-panel.css
│ │ │ └── main.css
│ │ ├── session
│ │ │ ├── room
│ │ │ │ ├── DisabledComposerView.js
│ │ │ │ ├── timeline
│ │ │ │ │ ├── MissingAttachmentView.js
│ │ │ │ │ ├── LocationView.js
│ │ │ │ │ ├── ImageView.js
│ │ │ │ │ ├── DateHeaderView.ts
│ │ │ │ │ ├── AnnouncementView.js
│ │ │ │ │ ├── FileView.js
│ │ │ │ │ ├── RedactedView.js
│ │ │ │ │ ├── ReactionsView.js
│ │ │ │ │ └── GapView.js
│ │ │ │ └── TimelineLoadingView.js
│ │ │ ├── rightpanel
│ │ │ │ ├── MemberTileView.js
│ │ │ │ └── MemberListView.js
│ │ │ └── SessionStatusView.js
│ │ ├── general
│ │ │ ├── LoadingView.js
│ │ │ ├── StaticView.js
│ │ │ └── types.ts
│ │ ├── login
│ │ │ ├── SessionLoadView.js
│ │ │ ├── CompleteSSOView.js
│ │ │ └── common.js
│ │ └── common.js
│ │ ├── utils
│ │ ├── Base58.js
│ │ ├── Encoding.js
│ │ └── Base64.js
│ │ ├── dom
│ │ ├── StorageEstimate.js
│ │ ├── UTF8.js
│ │ └── OnlineStatus.js
│ │ ├── LegacyPlatform.js
│ │ ├── sdk
│ │ └── paths
│ │ │ └── vite.js
│ │ ├── theming
│ │ ├── parsers
│ │ │ └── types.ts
│ │ └── shared
│ │ │ ├── svg-colorizer.mjs
│ │ │ └── color.mjs
│ │ ├── legacy-polyfill.js
│ │ └── index.html
├── domain
│ ├── session
│ │ ├── toast
│ │ │ ├── IToastCollection.ts
│ │ │ └── BaseToastNotificationViewModel.ts
│ │ ├── leftpanel
│ │ │ ├── common.js
│ │ │ └── RoomFilter.js
│ │ ├── common.js
│ │ ├── room
│ │ │ ├── timeline
│ │ │ │ ├── tiles
│ │ │ │ │ ├── RoomNameTile.js
│ │ │ │ │ ├── EncryptionEnabledTile.js
│ │ │ │ │ ├── MissingAttachmentTile.js
│ │ │ │ │ ├── ImageTile.js
│ │ │ │ │ ├── VideoTile.js
│ │ │ │ │ ├── RedactedTile.js
│ │ │ │ │ └── EncryptedEventTile.js
│ │ │ │ ├── UpdateAction.js
│ │ │ │ └── linkify
│ │ │ │ │ └── regex.ts
│ │ │ └── README.md
│ │ └── verification
│ │ │ └── stages
│ │ │ ├── MissingKeysViewModel.ts
│ │ │ ├── DismissibleVerificationViewModel.ts
│ │ │ └── VerifyEmojisViewModel.ts
│ ├── AvatarSource.ts
│ ├── ErrorViewModel.ts
│ └── login
│ │ └── StartSSOLoginViewModel.ts
├── index.html
├── matrix
│ ├── login
│ │ ├── index.ts
│ │ ├── LoginMethod.ts
│ │ ├── SSOLoginHelper.ts
│ │ ├── TokenLoginMethod.ts
│ │ └── PasswordLoginMethod.ts
│ ├── storage
│ │ ├── idb
│ │ │ ├── stores
│ │ │ │ ├── common.ts
│ │ │ │ ├── UserIdentityStore.ts
│ │ │ │ ├── OutboundGroupSessionStore.ts
│ │ │ │ ├── SharedSecretStore.ts
│ │ │ │ ├── AccountDataStore.ts
│ │ │ │ ├── InviteStore.ts
│ │ │ │ └── RoomSummaryStore.ts
│ │ │ ├── types.ts
│ │ │ ├── quirks.ts
│ │ │ └── export.ts
│ │ └── types.ts
│ ├── User.js
│ ├── verification
│ │ └── SAS
│ │ │ ├── VerificationCancelledError.ts
│ │ │ ├── stages
│ │ │ ├── constants.ts
│ │ │ ├── SendDoneStage.ts
│ │ │ ├── SendReadyStage.ts
│ │ │ ├── SendRequestVerificationStage.ts
│ │ │ └── SendKeyStage.ts
│ │ │ ├── types.ts
│ │ │ ├── channel
│ │ │ └── types.ts
│ │ │ ├── mac.ts
│ │ │ └── SASRequest.ts
│ ├── ServerFeatures.js
│ ├── registration
│ │ ├── stages
│ │ │ ├── DummyAuth.ts
│ │ │ ├── TermsAuth.ts
│ │ │ ├── TokenAuth.ts
│ │ │ └── BaseRegistrationStage.ts
│ │ └── types.ts
│ ├── e2ee
│ │ ├── megolm
│ │ │ └── decryption
│ │ │ │ ├── README.md
│ │ │ │ └── ReplayDetectionEntry.ts
│ │ └── olm
│ │ │ └── types.ts
│ ├── room
│ │ ├── timeline
│ │ │ ├── persistence
│ │ │ │ └── common.js
│ │ │ ├── Direction.ts
│ │ │ └── entries
│ │ │ │ └── NonPersistedEventEntry.js
│ │ ├── members
│ │ │ └── MemberList.js
│ │ ├── state
│ │ │ └── types.ts
│ │ └── joinRoom.ts
│ ├── net
│ │ └── types
│ │ │ └── response.ts
│ └── error.js
├── utils
│ ├── error.ts
│ ├── enum.ts
│ ├── RetainedValue.ts
│ ├── formatSize.ts
│ ├── Deferred.ts
│ ├── mergeMap.ts
│ ├── recursivelyAssign.ts
│ ├── sortedIndex.ts
│ ├── crypto
│ │ └── hkdf.ts
│ └── timeFormatting.ts
├── mocks
│ ├── poll.js
│ ├── Request.js
│ ├── event.js
│ └── HomeServer.js
├── observable
│ ├── index.ts
│ ├── value
│ │ ├── RetainedObservableValue.ts
│ │ ├── index.ts
│ │ └── EventObservableValue.ts
│ ├── map
│ │ ├── index.ts
│ │ └── ObservableValueMap.ts
│ └── list
│ │ └── common.ts
└── logging
│ ├── utils.ts
│ └── LogFilter.ts
├── .gitignore
├── LICENSE-COMMERCIAL
├── Dockerfile-dev
├── docker
└── dynamic-config.sh
├── .editorconfig
├── tsconfig-declaration.json
├── tsconfig.json
├── TODO.md
├── .github
├── CODEOWNERS
└── workflows
│ └── docker-publish.yml
├── codestyle.md
├── playwright.config.ts
├── .eslintrc.js
├── .ts-eslintrc.js
└── Dockerfile
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | target
3 |
--------------------------------------------------------------------------------
/prototypes/ie11.css:
--------------------------------------------------------------------------------
1 | p {
2 | color: red;
3 | }
--------------------------------------------------------------------------------
/prototypes/non-ie11.css:
--------------------------------------------------------------------------------
1 | p {
2 | color: green;
3 | }
4 |
--------------------------------------------------------------------------------
/scripts/sdk/test/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | yarn.lock
4 |
--------------------------------------------------------------------------------
/scripts/cleanup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # Remove icons created in .tmp
3 | rm -rf .tmp
4 |
--------------------------------------------------------------------------------
/prototypes/me.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/prototypes/me.jpg
--------------------------------------------------------------------------------
/prototypes/manifest.appcache:
--------------------------------------------------------------------------------
1 | CACHE MANIFEST
2 | # v1
3 | /responsive-layout-flex.html
4 | /me.jpg
5 |
--------------------------------------------------------------------------------
/scripts/package-overrides/safe-buffer/index.js:
--------------------------------------------------------------------------------
1 | import Buffer from "buffer";
2 | export {Buffer};
3 |
--------------------------------------------------------------------------------
/doc/architecture/UI/index.md:
--------------------------------------------------------------------------------
1 | # Index for UI code
2 |
3 | 1. [Rendering DOM elements](./render-dom-elements.md)
4 |
--------------------------------------------------------------------------------
/playwright/plugins/synapsedocker/templates/consent/README.md:
--------------------------------------------------------------------------------
1 | A synapse configured with user privacy consent enabled
2 |
--------------------------------------------------------------------------------
/playwright/plugins/synapsedocker/templates/default/README.md:
--------------------------------------------------------------------------------
1 | A synapse configured with user privacy consent disabled
2 |
--------------------------------------------------------------------------------
/src/platform/web/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/assets/icon.png
--------------------------------------------------------------------------------
/src/platform/web/public/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/public/icon.png
--------------------------------------------------------------------------------
/doc/problem solving/domexception_mapping.md:
--------------------------------------------------------------------------------
1 | err.name: explanation
2 | DataError: parameters to idb request where invalid
3 |
--------------------------------------------------------------------------------
/playwright/plugins/dex/template/dev.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/playwright/plugins/dex/template/dev.db
--------------------------------------------------------------------------------
/src/platform/web/assets/icon-maskable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/assets/icon-maskable.png
--------------------------------------------------------------------------------
/doc/architecture/images/coloring-process.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/doc/architecture/images/coloring-process.png
--------------------------------------------------------------------------------
/doc/architecture/images/svg-icon-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/doc/architecture/images/svg-icon-example.png
--------------------------------------------------------------------------------
/scripts/sdk/test/deps.d.ts:
--------------------------------------------------------------------------------
1 | // Keep TypeScripts from complaining about hydrogen-view-sdk not having types yet
2 | declare module "hydrogen-view-sdk";
3 |
--------------------------------------------------------------------------------
/doc/architecture/images/theming-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/doc/architecture/images/theming-architecture.png
--------------------------------------------------------------------------------
/playwright/plugins/synapsedocker/templates/COPYME/README.md:
--------------------------------------------------------------------------------
1 | # Meta-template for synapse templates
2 |
3 | To make another template, you can copy this directory
4 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Black.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Black.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Black.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Black.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Bold.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Bold.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Italic.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Light.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Light.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Medium.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Thin.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Thin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Thin.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Italic.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Medium.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Regular.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-Regular.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-SemiBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-SemiBold.woff
--------------------------------------------------------------------------------
/doc/implementation planning/html-messages.md:
--------------------------------------------------------------------------------
1 | message model:
2 | - paragraphs (p, h1, code block, quote, ...)
3 | - lines
4 | - parts (inline markup), which can be recursive
5 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-BlackItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-BlackItalic.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-BoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-BoldItalic.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-BoldItalic.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-ExtraBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-ExtraBold.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-ExtraBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-ExtraBold.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-ExtraLight.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-ExtraLight.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-ExtraLight.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-ExtraLight.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-LightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-LightItalic.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-SemiBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-SemiBold.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-ThinItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-ThinItalic.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-ThinItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-ThinItalic.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-BlackItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-BlackItalic.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-LightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-LightItalic.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-MediumItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-MediumItalic.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-MediumItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-MediumItalic.woff2
--------------------------------------------------------------------------------
/scripts/sdk/test/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test-sdk",
3 | "version": "0.0.0",
4 | "description": "",
5 | "dependencies": {
6 | "hydrogen-view-sdk": "link:../../../target"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-ExtraBoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-ExtraBoldItalic.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-ExtraBoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-ExtraBoldItalic.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-ExtraLightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-ExtraLightItalic.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-SemiBoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-SemiBoldItalic.woff
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-SemiBoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-SemiBoldItalic.woff2
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/inter/Inter-ExtraLightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/element-hq/hydrogen-web/HEAD/src/platform/web/ui/css/themes/element/inter/Inter-ExtraLightItalic.woff2
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sublime-project
2 | *.sublime-workspace
3 | .DS_Store
4 | node_modules
5 | fetchlogs
6 | sessionexports
7 | bundle.js
8 | target
9 | lib
10 | *.tar.gz
11 | .eslintcache
12 | .tmp
13 | playwright/synapselogs
14 |
--------------------------------------------------------------------------------
/doc/implementation planning/READ-RECEIPTS.md:
--------------------------------------------------------------------------------
1 | # Read receipts
2 |
3 | ## UI
4 |
5 | For the expanding avatars, trimmed at 5 or so, we could use css grid and switch from the right most cell to a cell that covers the whole width when clicking.
--------------------------------------------------------------------------------
/doc/implementation planning/timeline-member.md:
--------------------------------------------------------------------------------
1 | ## Get member for timeline event
2 |
3 | so when writing sync, we persist the display name and avatar
4 |
5 | the server might or might not support lazy loading
6 |
7 | if it is a room we just joined
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/README.md:
--------------------------------------------------------------------------------
1 | things that go in the theme:
2 | - margin specialization
3 | - padding
4 | - colors (foreground, background, border, ...)
5 | - border-radius
6 | - font faces, weights and sizes
7 | - alignment
8 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/e2ee-normal.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/playwright/plugins/synapsedocker/templates/consent/res/templates/privacy/en/success.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Test Privacy policy
5 |
6 |
7 | Danke schon
8 |
9 |
--------------------------------------------------------------------------------
/doc/implementation planning/LOGIN.md:
--------------------------------------------------------------------------------
1 | LoginView
2 | LoginViewModel
3 | SessionPickerView
4 | SessionPickerViewModel
5 |
6 | matrix:
7 | SessionStorage (could be in keychain, ... for now we go with localstorage)
8 | getAll()
9 |
10 | Login
11 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/chevron-right.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/LICENSE-COMMERCIAL:
--------------------------------------------------------------------------------
1 | Licensees holding a valid commercial license with Element may use this
2 | software in accordance with the terms contained in a written agreement
3 | between you and Element.
4 |
5 | To purchase a commercial license please contact our sales team at
6 | licensing@element.io
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/chevron-thin-left.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/domain/session/toast/IToastCollection.ts:
--------------------------------------------------------------------------------
1 | import {ObservableArray} from "../../../observable";
2 | import type {BaseToastNotificationViewModel} from "./BaseToastNotificationViewModel";
3 |
4 | export interface IToastCollection {
5 | toastViewModels: ObservableArray;
6 | }
7 |
--------------------------------------------------------------------------------
/scripts/test-derived-theme/test-theme.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | cp scripts/test-derived-theme/theme.json target/assets/theme-customer.json
3 | cat target/config.json | jq '.themeManifests += ["assets/theme-customer.json"]' | cat > target/config.temp.json
4 | rm target/config.json
5 | mv target/config.temp.json target/config.json
6 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/matrix/login/index.ts:
--------------------------------------------------------------------------------
1 | import {ILoginMethod} from "./LoginMethod";
2 | import {PasswordLoginMethod} from "./PasswordLoginMethod";
3 | import {SSOLoginHelper} from "./SSOLoginHelper";
4 | import {TokenLoginMethod} from "./TokenLoginMethod";
5 |
6 |
7 | export {PasswordLoginMethod, SSOLoginHelper, TokenLoginMethod, ILoginMethod};
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/encryption-status.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/doc/implementation planning/DESIGN.md:
--------------------------------------------------------------------------------
1 | use mock view models or even a mock session to render different states of the app in a static html document, where we can somehow easily tweak the css (just browser tools, or do something in the page?) how to persist css after changes?
2 |
3 | Also dialogs, forms, ... could be shown on this page.
4 |
--------------------------------------------------------------------------------
/scripts/package.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | VERSION=$(jq -r ".version" package.json)
3 | PACKAGE=hydrogen-web-$VERSION.tar.gz
4 | yarn build
5 | pushd target
6 | # move config file so we don't override it
7 | # when deploying a new version
8 | mv config.json config.sample.json
9 | tar -czvf ../$PACKAGE ./
10 | popd
11 | echo $PACKAGE
12 |
--------------------------------------------------------------------------------
/scripts/package-overrides/buffer/index.js:
--------------------------------------------------------------------------------
1 | var Buffer = {
2 | isBuffer: function(array) {return array instanceof Uint8Array;},
3 | from: function(arrayBuffer) {return arrayBuffer;},
4 | allocUnsafe: function(size) {return Buffer.alloc(size);},
5 | alloc: function(size) {return new Uint8Array(size);}
6 | };
7 | export default Buffer;
8 |
--------------------------------------------------------------------------------
/Dockerfile-dev:
--------------------------------------------------------------------------------
1 | FROM docker.io/node:alpine
2 | RUN apk add --no-cache git python3 build-base
3 |
4 | WORKDIR /code
5 |
6 | # Copy package.json and yarn.lock and install dependencies first to speed up subsequent builds
7 | COPY package.json yarn.lock /code/
8 | RUN yarn install
9 |
10 | COPY . /code
11 | EXPOSE 3000
12 | ENTRYPOINT ["yarn", "start"]
13 |
--------------------------------------------------------------------------------
/docker/dynamic-config.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -eux
4 |
5 | if [ -n "${CONFIG_OVERRIDE:-}" ]; then
6 | # Use config override environment variable if set
7 | echo "$CONFIG_OVERRIDE" > /tmp/config.json
8 | else
9 | # Otherwise, use the default config that was bundled in the image
10 | cp /config.json.bundled /tmp/config.json
11 | fi
12 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/clear.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | # top-most EditorConfig file
3 | root = true
4 |
5 | # Unix-style newlines with a newline ending every file
6 | [*]
7 | end_of_line = lf
8 | insert_final_newline = true
9 | charset = utf-8
10 | indent_style = space
11 | indent_size = 4
12 |
13 | # Matches multiple files with brace expansion notation
14 | # Set default charset
15 | # [*.{js,py}]
16 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/dismiss.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/popup.css:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | .popupContainer {
10 | position: absolute;
11 | white-space: nowrap;
12 | }
13 |
--------------------------------------------------------------------------------
/src/matrix/storage/idb/stores/common.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export const MIN_UNICODE = "\u{0}";
10 | export const MAX_UNICODE = "\u{10FFFF}";
11 |
--------------------------------------------------------------------------------
/tsconfig-declaration.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "noEmit": false,
5 | "emitDeclarationOnly": true,
6 | "declaration": true,
7 | "outDir": "target/types",
8 | "rootDir": "src"
9 | },
10 | "exclude": [
11 | "src/sdk/paths/*"
12 | ],
13 | "include": ["src/**/*"],
14 | }
15 |
--------------------------------------------------------------------------------
/scripts/sdk/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Vite App
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/platform/web/assets/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "push": {
3 | "appId": "io.element.hydrogen.web",
4 | "gatewayUrl": "https://matrix.org",
5 | "applicationServerKey": "BC-gpSdVHEXhvHSHS0AzzWrQoukv2BE7KzpoPO_FfPacqOo3l1pdqz7rSgmB04pZCWaHPz7XRe6fjLaC-WPDopM"
6 | },
7 | "defaultHomeServer": "matrix.org",
8 | "bugReportEndpointUrl": "https://element.io/bugreports/submit"
9 | }
10 |
--------------------------------------------------------------------------------
/doc/architecture/persisted-network-calls.md:
--------------------------------------------------------------------------------
1 | # General Pattern of implementing a persisted network call
2 |
3 | 1. do network request
4 | 1. start transaction
5 | 1. write result of network request into transaction store, keeping differences from previous store state in local variables
6 | 1. close transaction
7 | 1. apply differences applied to store to in-memory data
8 | 1. emit events for changes
9 |
--------------------------------------------------------------------------------
/src/utils/error.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export class AbortError extends Error {
10 | get name(): string {
11 | return "AbortError";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strictNullChecks": true,
4 | "noImplicitAny": false,
5 | "noEmit": true,
6 | "target": "ES2020",
7 | "module": "ES2020",
8 | "moduleResolution": "node",
9 | "esModuleInterop": true
10 | },
11 | "exclude": [
12 | "src/sdk/paths/*"
13 | ],
14 | "include": ["src/**/*"],
15 | }
16 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | - make it a copy, not a fork of brawl, so we can have issues
2 | - add compilation step for ie11 compatible bundle
3 | - compile to es5
4 | - use bluebird for promises
5 | - make xhr request impl
6 | - once app is loading, go over errors
7 |
8 |
9 | - project goals
10 | - works on mobile
11 | - works well offline
12 | - components can be used in isolation
13 | - lazyload components?
--------------------------------------------------------------------------------
/doc/problem solving/IMPORT-ISSUES.md:
--------------------------------------------------------------------------------
1 | ## How to import common-js dependency using ES6 syntax
2 | ---
3 | Until [#6632](https://github.com/vitejs/vite/issues/6632) is fixed, such imports should be done as follows:
4 |
5 | ```ts
6 | import * as pkg from "off-color";
7 | // @ts-ignore
8 | const offColor = pkg.offColor ?? pkg.default.offColor;
9 | ```
10 |
11 | This way build, dev server and unit tests should all work.
12 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Docker related files are not maintained by the core Hydrogen team
2 | /.dockerignore @hughns @sandhose
3 | /Dockerfile @hughns @sandhose
4 | /Dockerfile-dev @hughns @sandhose
5 | /.github/workflows/docker-publish.yml @hughns @sandhose
6 | /docker/ @hughns @sandhose
7 | /doc/docker.md @hughns @sandhose
8 |
--------------------------------------------------------------------------------
/src/matrix/User.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export class User {
10 | constructor(userId) {
11 | this._userId = userId;
12 | }
13 |
14 | get id() {
15 | return this._userId;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/scripts/sdk/test/test-sdk-in-esm-vite-build-env.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path');
2 | const { build } = require('vite');
3 |
4 | async function main() {
5 | await build({
6 | outDir: './dist',
7 | build: {
8 | rollupOptions: {
9 | input: {
10 | main: resolve(__dirname, 'index.html')
11 | }
12 | }
13 | }
14 | });
15 |
16 | console.log('SDK works in Vite build ✅');
17 | }
18 |
19 | main();
20 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/video-call.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/scripts/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "node": true,
4 | "es6": true
5 | },
6 | "extends": "eslint:recommended",
7 | "parserOptions": {
8 | "ecmaVersion": 2020,
9 | "sourceType": "module"
10 | },
11 | "rules": {
12 | "no-console": "off",
13 | "no-empty": "off",
14 | "no-prototype-builtins": "off",
15 | "no-unused-vars": "warn"
16 | },
17 | };
18 |
19 |
--------------------------------------------------------------------------------
/src/domain/session/leftpanel/common.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export function comparePrimitive(a, b) {
10 | if (a === b) {
11 | return 0;
12 | } else {
13 | return a < b ? -1 : 1;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/scripts/release.sh:
--------------------------------------------------------------------------------
1 | set -e
2 | if [ -z "$1" ]; then
3 | echo "provide a new version, current version is $(jq '.version' package.json)"
4 | exit 1
5 | fi
6 | VERSION=$1
7 | git checkout master
8 | git pull --rebase origin master
9 | jq ".version = \"$VERSION\"" package.json > package.json.tmp
10 | rm package.json
11 | mv package.json.tmp package.json
12 | git add package.json
13 | git commit -m "release v$VERSION"
14 | git tag "v$VERSION"
15 | git push --tags origin master
16 |
--------------------------------------------------------------------------------
/src/utils/enum.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export function createEnum(...values: string[]): Readonly<{}> {
10 | const obj = {};
11 | for (const value of values) {
12 | obj[value] = value;
13 | }
14 | return Object.freeze(obj);
15 | }
16 |
--------------------------------------------------------------------------------
/src/domain/session/common.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export function imageToInfo(image) {
10 | return {
11 | w: image.width,
12 | h: image.height,
13 | mimetype: image.blob.mimeType,
14 | size: image.blob.size
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/src/platform/web/assets/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Hydrogen",
3 | "short_name": "Hydrogen",
4 | "display": "standalone",
5 | "description": "Lightweight matrix client with legacy and mobile browser support",
6 | "start_url": "../index.html",
7 | "icons": [
8 | {"src": "icon.png", "sizes": "384x384", "type": "image/png"},
9 | {"src": "icon-maskable.png", "sizes": "384x384", "type": "image/png", "purpose": "maskable"}
10 | ],
11 | "theme_color": "#0DBD8B"
12 | }
13 |
--------------------------------------------------------------------------------
/src/platform/web/utils/Base58.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import bs58 from "bs58";
10 |
11 | export class Base58 {
12 | encode(buffer) {
13 | return bs58.encode(buffer);
14 | }
15 |
16 | decode(str) {
17 | return bs58.decode(str);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/playwright/tests/startup.spec.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2022 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import {test} from '@playwright/test';
9 |
10 | test("App has no startup errors that prevent UI render", async ({ page }) => {
11 | await page.goto("/");
12 | await page.getByText("Log In", { exact: true }).waitFor();
13 | });
14 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/status.css:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 | Copyright 2020 The Matrix.org Foundation C.I.C.
5 |
6 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
7 | Please see LICENSE files in the repository root for full details.
8 | */
9 |
10 | .SessionStatusView {
11 | display: flex;
12 | }
13 |
14 | .SessionStatusView p {
15 | margin: 0 10px;
16 | word-break: break-all;
17 | word-break: break-word;
18 | }
19 |
--------------------------------------------------------------------------------
/src/matrix/storage/idb/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export interface IDOMStorage {
10 | getItem(key: string): string | null;
11 | setItem(key: string, value: string): void;
12 | removeItem(key: string): void;
13 | key(n: number): string | null;
14 | readonly length: number;
15 | }
16 |
--------------------------------------------------------------------------------
/src/domain/AvatarSource.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
4 | Copyright 2020 Bruno Windels
5 |
6 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
7 | Please see LICENSE files in the repository root for full details.
8 | */
9 |
10 | export interface AvatarSource {
11 | get avatarLetter(): string;
12 | get avatarColorNumber(): number;
13 | avatarUrl(size: number): string | undefined;
14 | get avatarTitle(): string;
15 | }
16 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/chevron-small.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/scripts/test-app.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Make sure docker is available
4 | if ! docker --version > /dev/null 2>&1; then
5 | echo "You need to intall docker before you can run the tests!"
6 | exit 1
7 | fi
8 |
9 | # Stop running containers
10 | if docker stop hydrogen-synapse > /dev/null 2>&1; then
11 | echo "Existing 'hydrogen-synapse' container stopped ✔"
12 | fi
13 |
14 | if docker stop hydrogen-dex > /dev/null 2>&1; then
15 | echo "Existing 'hydrogen-dex' container stopped ✔"
16 | fi
17 |
18 | # Run playwright
19 | yarn playwright test
20 |
--------------------------------------------------------------------------------
/src/matrix/verification/SAS/VerificationCancelledError.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export class VerificationCancelledError extends Error {
10 | get name(): string {
11 | return "VerificationCancelledError";
12 | }
13 |
14 | get message(): string {
15 | return "Verification is cancelled!";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/info.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/matrix/ServerFeatures.js:
--------------------------------------------------------------------------------
1 | const R0_5_0 = "r0.5.0";
2 |
3 | export class ServerFeatures {
4 | constructor(versionResponse) {
5 | this._versionResponse = versionResponse;
6 | }
7 |
8 | _supportsVersion(version) {
9 | if (!this._versionResponse) {
10 | return false;
11 | }
12 | const {versions} = this._versionResponse;
13 | return Array.isArray(versions) && versions.includes(version);
14 | }
15 |
16 | get lazyLoadMembers() {
17 | return this._supportsVersion(R0_5_0);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/room/DisabledComposerView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {TemplateView} from "../../general/TemplateView";
10 |
11 | export class DisabledComposerView extends TemplateView {
12 | render(t) {
13 | return t.div({className: "DisabledComposerView"}, t.h3(vm => vm.description));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/font.css:
--------------------------------------------------------------------------------
1 | /** from https://gist.github.com/mfornos/9991865 */
2 |
3 | @font-face {
4 | font-family: 'emoji';
5 | src: local('Apple Color Emoji'),
6 | local('Segoe UI Emoji'),
7 | local('Segoe UI Symbol'),
8 | local('Noto Color Emoji'),
9 | local('Twemoji'),
10 | local('Twemoji Mozilla'),
11 | local('Android Emoji'),
12 | local('EmojiSymbols'),
13 | local('Symbola');
14 |
15 | /* Emoji unicode blocks */
16 | unicode-range: U+1F300-1F5FF, U+1F600-1F64F, U+1F680-1F6FF, U+2600-26FF;
17 | }
18 |
--------------------------------------------------------------------------------
/src/platform/web/dom/StorageEstimate.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export async function estimateStorageUsage() {
10 | if (navigator?.storage?.estimate) {
11 | const {quota, usage} = await navigator.storage.estimate();
12 | return {quota, usage};
13 | } else {
14 | return {quota: null, usage: null};
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/scripts/sdk/test/test-sdk-in-commonjs-env.js:
--------------------------------------------------------------------------------
1 | // Make sure the SDK can be used in a CommonJS environment.
2 | // Usage: node scripts/sdk/test/test-sdk-in-commonjs-env.js
3 | const hydrogenViewSdk = require('hydrogen-view-sdk');
4 |
5 | // Test that the "exports" are available:
6 | // Worker
7 | require.resolve('hydrogen-view-sdk/main.js');
8 | // Styles
9 | require.resolve('hydrogen-view-sdk/assets/theme-element-light.css');
10 | // Can access files in the assets/* directory
11 | require.resolve('hydrogen-view-sdk/assets/main.js');
12 |
13 | console.log('SDK works in CommonJS ✅');
14 |
--------------------------------------------------------------------------------
/src/matrix/login/LoginMethod.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import type {ILogItem} from "../../logging/types";
10 | import type {HomeServerApi} from "../net/HomeServerApi.js";
11 |
12 | export interface ILoginMethod {
13 | homeserver: string;
14 | login(hsApi: HomeServerApi, deviceName: string, log: ILogItem): Promise>;
15 | }
16 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/room/timeline/MissingAttachmentView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {BaseMessageView} from "./BaseMessageView.js";
10 |
11 | export class MissingAttachmentView extends BaseMessageView {
12 | renderMessageBody(t, vm) {
13 | return t.p({className: "Timeline_messageBody statusMessage"}, vm.label);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/mocks/poll.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export async function poll(fn) {
10 | do {
11 | const result = fn();
12 | if (result) {
13 | return result;
14 | } else {
15 | await new Promise(setImmediate); //eslint-disable-line no-undef
16 | }
17 | } while (1); //eslint-disable-line no-constant-condition
18 | }
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/paperclip.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/doc/implementation planning/RELEASE.md:
--------------------------------------------------------------------------------
1 | release:
2 | - bundling css files
3 | - bundling javascript
4 | - run index.html template for release as opposed to develop version?
5 | - make list of all resources needed (images, html page)
6 | - create appcache manifest + service worker
7 | - create tarball + sign
8 | - make gh release with tarball + signature
9 | publish:
10 | - extract tarball
11 | - upload to static website
12 | - overwrite index.html
13 | - overwrite service worker & appcache manifest
14 | - put new version files under /x.x.x
15 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/cam-unmuted.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/platform/web/utils/Encoding.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {UTF8} from "../dom/UTF8.js";
10 | import {Base64} from "./Base64.js";
11 | import {Base58} from "./Base58.js";
12 |
13 | export class Encoding {
14 | constructor() {
15 | this.utf8 = new UTF8();
16 | this.base64 = new Base64();
17 | this.base58 = new Base58();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/domain/session/leftpanel/RoomFilter.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export class RoomFilter {
10 | constructor(query) {
11 | this._parts = query.split(" ").map(s => s.toLowerCase().trim());
12 | }
13 |
14 | matches(roomTileVM) {
15 | const name = roomTileVM.name.toLowerCase();
16 | return this._parts.every(p => name.includes(p));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/playwright/global-setup.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2022 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | const env = {
10 | SYNAPSE_IP_ADDRESS: "172.18.0.5",
11 | SYNAPSE_PORT: "8008",
12 | DEX_IP_ADDRESS: "172.18.0.4",
13 | DEX_PORT: "5556",
14 | }
15 |
16 | export default function setupEnvironmentVariables() {
17 | for (const [key, value] of Object.entries(env)) {
18 | process.env[key] = value;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/matrix/storage/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export type Content = { [key: string]: any }
10 |
11 | export interface TimelineEvent {
12 | content: Content;
13 | type: string;
14 | event_id: string;
15 | sender: string;
16 | origin_server_ts: number;
17 | unsigned?: Content;
18 | }
19 |
20 | export type StateEvent = TimelineEvent & { prev_content?: Content, state_key: string }
21 |
--------------------------------------------------------------------------------
/src/platform/web/ui/general/LoadingView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {StaticView} from "./StaticView";
10 | import {spinner} from "../common.js";
11 |
12 | export class LoadingView extends StaticView {
13 | constructor(label = "Loading") {
14 | super(label, (t, label) => {
15 | return t.div({ className: "LoadingView" }, [spinner(t), label]);
16 | });
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/form.css:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 | Copyright 2020 The Matrix.org Foundation C.I.C.
5 |
6 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
7 | Please see LICENSE files in the repository root for full details.
8 | */
9 |
10 | .form-row.text > input, .form-row.text > textarea {
11 | display: block;
12 | width: 100%;
13 | min-width: 0;
14 | box-sizing: border-box;
15 | }
16 |
17 | .FilterField {
18 | display: flex;
19 | }
20 |
21 | .FilterField input {
22 | display: block;
23 | flex: 1;
24 | min-width: 0;
25 | }
26 |
--------------------------------------------------------------------------------
/src/platform/web/LegacyPlatform.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import aesjs from "aes-js";
10 | import {hkdf} from "../../utils/crypto/hkdf";
11 |
12 | import {Platform as ModernPlatform} from "./Platform.js";
13 |
14 | export function Platform({ container, assetPaths, config, configURL, options = null }) {
15 | return new ModernPlatform({ container, assetPaths, config, configURL, options, cryptoExtras: { aesjs, hkdf }});
16 | }
17 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/send.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/doc/implementation planning/session-container.md:
--------------------------------------------------------------------------------
1 | what should this new container be called?
2 | - Client
3 | - SessionContainer
4 |
5 |
6 | it is what is returned from bootstrapping a ... thing
7 | it allows you to replace classes within the client through IoC?
8 | it wires up the different components
9 | it unwires the components when you're done with the thing
10 | it could hold all the dependencies for setting up a client, even before login
11 | - online detection api
12 | - clock
13 | - homeserver
14 | - requestFn
15 |
16 | we'll be explicitly making its parts public though, like session, sync, reconnector
17 |
18 | merge the connectionstate and
19 |
--------------------------------------------------------------------------------
/codestyle.md:
--------------------------------------------------------------------------------
1 |
2 | # Code-style
3 |
4 | - methods that return a promise should always use async/await
5 | otherwise synchronous errors can get swallowed
6 | you can return a promise without awaiting it though.
7 | - only named exports, no default exports
8 | otherwise it becomes hard to remember what was a default/named export
9 | - should we return promises from storage mutation calls? probably not, as we don't await them anywhere. only read calls should return promises?
10 | - we don't anymore
11 | - don't use these features, as they are not widely enough supported.
12 | - [lookbehind in regular expressions](https://caniuse.com/js-regexp-lookbehind)
13 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/disable-grid.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/matrix/verification/SAS/stages/constants.ts:
--------------------------------------------------------------------------------
1 | // From element-web
2 | export type KeyAgreement = "curve25519-hkdf-sha256" | "curve25519";
3 | export type MacMethod = "hkdf-hmac-sha256.v2" | "org.matrix.msc3783.hkdf-hmac-sha256" | "hkdf-hmac-sha256" | "hmac-sha256";
4 |
5 | export const KEY_AGREEMENT_LIST: KeyAgreement[] = ["curve25519-hkdf-sha256", "curve25519"];
6 | export const HASHES_LIST = ["sha256"];
7 | export const MAC_LIST: MacMethod[] = [
8 | "hkdf-hmac-sha256.v2",
9 | "org.matrix.msc3783.hkdf-hmac-sha256",
10 | "hkdf-hmac-sha256",
11 | "hmac-sha256",
12 | ];
13 | export const SAS_LIST = ["decimal", "emoji"];
14 | export const SAS_SET = new Set(SAS_LIST);
15 |
--------------------------------------------------------------------------------
/src/platform/web/sdk/paths/vite.js:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import _downloadSandboxPath from "../../assets/download-sandbox.html?url";
3 | // @ts-ignore
4 | import _workerPath from "../../worker/main.js?url";
5 | // @ts-ignore
6 | import olmWasmPath from "@matrix-org/olm/olm.wasm?url";
7 | // @ts-ignore
8 | import olmJsPath from "@matrix-org/olm/olm.js?url";
9 | // @ts-ignore
10 | import olmLegacyJsPath from "@matrix-org/olm/olm_legacy.js?url";
11 |
12 | export default {
13 | downloadSandbox: _downloadSandboxPath,
14 | worker: _workerPath,
15 | olm: {
16 | wasm: olmWasmPath,
17 | legacyBundle: olmLegacyJsPath,
18 | wasmBundle: olmJsPath,
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import type { PlaywrightTestConfig } from "@playwright/test";
2 |
3 | const BASE_URL = process.env["BASE_URL"] ?? "http://127.0.0.1:3000";
4 |
5 | const config: PlaywrightTestConfig = {
6 | use: {
7 | headless: false,
8 | viewport: { width: 1280, height: 720 },
9 | ignoreHTTPSErrors: true,
10 | video: "on-first-retry",
11 | baseURL: BASE_URL,
12 | },
13 | testDir: "./playwright/tests",
14 | globalSetup: require.resolve("./playwright/global-setup"),
15 | webServer: {
16 | command: "yarn start",
17 | url: `${BASE_URL}/#/login`,
18 | },
19 | workers: 1
20 | };
21 | export default config;
22 |
--------------------------------------------------------------------------------
/src/domain/session/room/timeline/tiles/RoomNameTile.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {SimpleTile} from "./SimpleTile";
10 |
11 | export class RoomNameTile extends SimpleTile {
12 |
13 | get shape() {
14 | return "announcement";
15 | }
16 |
17 | get announcement() {
18 | const content = this._entry.content;
19 | return `${this._entry.displayName || this._entry.sender} named the room "${content?.name}"`
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/domain/session/verification/stages/MissingKeysViewModel.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {ViewModel, Options} from "../../../ViewModel";
10 | import type {SegmentType} from "../../../navigation/index";
11 |
12 | export class MissingKeysViewModel extends ViewModel {
13 | gotoSettings() {
14 | this.navigation.push("settings", true);
15 | }
16 |
17 | get kind(): string {
18 | return "keys-missing";
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/prototypes/tools/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "foo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "@babel/core": "^7.11.1",
13 | "@babel/preset-env": "^7.11.0",
14 | "@rollup/plugin-babel": "^5.1.0",
15 | "@rollup/plugin-commonjs": "^15.0.0",
16 | "@rollup/plugin-multi-entry": "^4.0.0",
17 | "@rollup/plugin-node-resolve": "^9.0.0",
18 | "mdn-polyfills": "^5.20.0",
19 | "regenerator-runtime": "^0.13.7",
20 | "rollup": "^2.26.4",
21 | "core-js": "^3.6.5"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/domain/session/room/timeline/tiles/EncryptionEnabledTile.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {SimpleTile} from "./SimpleTile";
10 |
11 | export class EncryptionEnabledTile extends SimpleTile {
12 | get shape() {
13 | return "announcement";
14 | }
15 |
16 | get announcement() {
17 | const senderName = this._entry.displayName || this._entry.sender;
18 | return this.i18n`${senderName} has enabled end-to-end encryption`;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/verified.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/observable/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 |
10 | // re-export "root" (of chain) collection
11 | export { ObservableMap, ApplyMap, FilteredMap, JoinedMap, LogMap, MappedMap } from "./map";
12 | export { ObservableArray } from "./list/ObservableArray";
13 | export { SortedArray } from "./list/SortedArray";
14 | export { MappedList } from "./list/MappedList";
15 | export { AsyncMappedList } from "./list/AsyncMappedList";
16 | export { ConcatList } from "./list/ConcatList";
17 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/e2ee-disabled.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/plus.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/matrix/login/SSOLoginHelper.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export class SSOLoginHelper{
10 | private _homeserver: string;
11 |
12 | constructor(homeserver: string) {
13 | this._homeserver = homeserver;
14 | }
15 |
16 | get homeserver(): string { return this._homeserver; }
17 |
18 | createSSORedirectURL(returnURL: string): string {
19 | return `${this._homeserver}/_matrix/client/r0/login/sso/redirect?redirectUrl=${encodeURIComponent(returnURL)}`;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/verification-error.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/room/TimelineLoadingView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {TemplateView} from "../../general/TemplateView";
10 | import {spinner} from "../../common.js";
11 |
12 | export class TimelineLoadingView extends TemplateView {
13 | render(t, vm) {
14 | return t.div({className: "TimelineLoadingView"}, [
15 | spinner(t),
16 | t.div(vm.isEncrypted ? vm.i18n`Loading encrypted messages…` : vm.i18n`Loading messages…`)
17 | ]);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/playwright/plugins/synapsedocker/templates/consent/res/templates/privacy/en/1.0.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Test Privacy policy
5 |
6 |
7 | {% if has_consented %}
8 |
9 | Thank you, you've already accepted the license.
10 |
11 | {% else %}
12 |
13 | Please accept the license!
14 |
15 |
21 | {% endif %}
22 |
23 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/vertical-ellipsis.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/matrix/registration/stages/DummyAuth.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {AuthenticationData} from "../types";
10 | import {BaseRegistrationStage} from "./BaseRegistrationStage";
11 |
12 | export class DummyAuth extends BaseRegistrationStage {
13 | generateAuthenticationData(): AuthenticationData {
14 | return {
15 | session: this._session,
16 | type: this.type,
17 | };
18 | }
19 |
20 | get type(): string {
21 | return "m.login.dummy";
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/chevron-left.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/matrix/verification/SAS/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import type {IChannel} from "./channel/IChannel";
9 | import type {CalculateSASStage} from "./stages/CalculateSASStage";
10 | import type {SelectVerificationMethodStage} from "./stages/SelectVerificationMethodStage";
11 |
12 | export type SASProgressEvents = {
13 | SelectVerificationStage: SelectVerificationMethodStage;
14 | EmojiGenerated: CalculateSASStage;
15 | VerificationCompleted: string;
16 | VerificationCancelled: IChannel["cancellation"];
17 | }
18 |
--------------------------------------------------------------------------------
/src/platform/web/theming/parsers/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export type NormalVariant = {
10 | id: string;
11 | cssLocation: string;
12 | variables?: any;
13 | };
14 |
15 | export type Variant = NormalVariant & {
16 | variantName: string;
17 | };
18 |
19 | export type DefaultVariant = {
20 | dark: Variant;
21 | light: Variant;
22 | default: Variant;
23 | }
24 |
25 | export type ThemeInformation = NormalVariant | DefaultVariant;
26 |
27 | export enum ColorSchemePreference {
28 | Dark,
29 | Light
30 | };
31 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/room/timeline/LocationView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {BaseMessageView} from "./BaseMessageView.js";
10 |
11 | export class LocationView extends BaseMessageView {
12 | renderMessageBody(t, vm) {
13 | return t.p({className: "Timeline_messageBody statusMessage"}, [
14 | t.span(vm.label),
15 | t.a({className: "Timeline_locationLink", href: vm.mapsLink, target: "_blank", rel: "noopener"}, vm.i18n`Open in maps`),
16 | t.time(vm.time)
17 | ]);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/scripts/postcss/tests/common.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | const postcss = require("postcss");
10 |
11 | module.exports.createTestRunner = function (plugin) {
12 | return async function run(input, output, opts = {}, assert) {
13 | let result = await postcss([plugin(opts)]).process(input, { from: undefined, });
14 | assert.strictEqual(
15 | result.css.replaceAll(/\s/g, ""),
16 | output.replaceAll(/\s/g, "")
17 | );
18 | assert.strictEqual(result.warnings().length, 0);
19 | };
20 | }
21 |
22 |
23 |
--------------------------------------------------------------------------------
/doc/implementation planning/background-tasks.md:
--------------------------------------------------------------------------------
1 | we make the current session status bar float and display generally short messages for all background tasks like:
2 | "Waiting Xs to reconnect... [try now]"
3 | "Reconnecting..."
4 | "Sending message 1 of 10..."
5 |
6 | As it is floating, it doesn't pop they layout and mess up the scroll offset of the timeline.
7 | Need to find a good place to float it though. Preferably on top for visibility, but it could occlude the room header. Perhaps bottom left?
8 |
9 | If more than 1 background thing is going on at the same time we display (1/x).
10 | If you click the button status bar anywhere, it takes you to a page adjacent to the room view (and e.g. in the future the settings) and you get an overview of all running background tasks.
11 |
--------------------------------------------------------------------------------
/prototypes/ie11-hashchange.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
18 |
19 | foo
20 | bar
21 | baz
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/platform/web/dom/UTF8.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | export class UTF8 {
9 | constructor() {
10 | this._encoder = null;
11 | this._decoder = null;
12 | }
13 |
14 | encode(str) {
15 | if (!this._encoder) {
16 | this._encoder = new TextEncoder();
17 | }
18 | return this._encoder.encode(str);
19 | }
20 |
21 | decode(buffer) {
22 | if (!this._decoder) {
23 | this._decoder = new TextDecoder();
24 | }
25 | return this._decoder.decode(buffer);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/utils/RetainedValue.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export class RetainedValue {
10 | private readonly _freeCallback: () => void;
11 | private _retentionCount: number = 1;
12 |
13 | constructor(freeCallback: () => void) {
14 | this._freeCallback = freeCallback;
15 | }
16 |
17 | retain(): void {
18 | this._retentionCount += 1;
19 | }
20 |
21 | release(): void {
22 | this._retentionCount -= 1;
23 | if (this._retentionCount === 0) {
24 | this._freeCallback();
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/room/timeline/ImageView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {BaseMediaView} from "./BaseMediaView.js";
10 |
11 | export class ImageView extends BaseMediaView {
12 | renderMedia(t, vm) {
13 | const img = t.img({
14 | src: vm => vm.thumbnailUrl,
15 | alt: vm => vm.label,
16 | title: vm => vm.label,
17 | style: `max-width: ${vm.width}px; max-height: ${vm.height}px;`
18 | });
19 | return vm.isPending || !vm.lightboxUrl ? img : t.a({href: vm.lightboxUrl}, img);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/observable/value/RetainedObservableValue.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {ObservableValue} from "./index";
10 |
11 | export class RetainedObservableValue extends ObservableValue {
12 |
13 | constructor(initialValue: T, private freeCallback: () => void, private startCallback: () => void = () => {}) {
14 | super(initialValue);
15 | }
16 |
17 | onSubscribeFirst(): void {
18 | this.startCallback();
19 | }
20 |
21 | onUnsubscribeLast(): void {
22 | super.onUnsubscribeLast();
23 | this.freeCallback();
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/rightpanel/MemberTileView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {TemplateView} from "../../general/TemplateView";
10 | import {AvatarView} from "../../AvatarView.js";
11 |
12 | export class MemberTileView extends TemplateView {
13 | render(t, vm) {
14 | return t.li({ className: "MemberTileView" },
15 | t.a({ href: vm.detailsUrl },
16 | [
17 | t.view(new AvatarView(vm, 32)),
18 | t.div({ className: "MemberTileView_name" }, (vm) => vm.name),
19 | ])
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/platform/web/theming/shared/svg-colorizer.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export function getColoredSvgString(svgString, primaryColor, secondaryColor) {
10 | let coloredSVGCode = svgString.replaceAll("#ff00ff", primaryColor);
11 | coloredSVGCode = coloredSVGCode.replaceAll("#00ffff", secondaryColor);
12 | if (svgString === coloredSVGCode) {
13 | throw new Error("svg-colorizer made no color replacements! The input svg should only contain colors #ff00ff (primary, case-sensitive) and #00ffff (secondary, case-sensitive).");
14 | }
15 | return coloredSVGCode;
16 | }
17 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/room/timeline/DateHeaderView.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {TemplateView} from "../../../general/TemplateView";
10 | import type {DateTile} from "../../../../../../domain/session/room/timeline/tiles/DateTile";
11 |
12 | export class DateHeaderView extends TemplateView {
13 | render(t, vm) {
14 | return t.h2({className: "DateHeader"}, t.time({dateTime: vm.machineReadableDate}, vm.relativeDate));
15 | }
16 |
17 | /* This is called by the parent ListView, which just has 1 listener for the whole list */
18 | onClick() {}
19 | }
20 |
--------------------------------------------------------------------------------
/src/platform/web/utils/Base64.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import base64 from "base64-arraybuffer";
10 |
11 | export class Base64 {
12 | encodeUnpadded(buffer) {
13 | const str = base64.encode(buffer);
14 | const paddingIdx = str.indexOf("=");
15 | if (paddingIdx !== -1) {
16 | return str.substr(0, paddingIdx);
17 | } else {
18 | return str;
19 | }
20 | }
21 |
22 | encode(buffer) {
23 | return base64.encode(buffer);
24 | }
25 |
26 | decode(str) {
27 | return base64.decode(str);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/scripts/sdk/create-manifest.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const fs = require("fs");
3 | const appManifest = require("../../package.json");
4 | const baseSDKManifest = require("./base-manifest.json");
5 | /*
6 | Need to leave typescript type definitions out until the
7 | typescript conversion is complete and all imports in the d.ts files
8 | exists.
9 | ```
10 | "types": "types/lib.d.ts"
11 | ```
12 | */
13 | const mergeOptions = require('merge-options');
14 |
15 | const manifestExtension = {
16 | devDependencies: undefined,
17 | scripts: undefined,
18 | };
19 |
20 | const manifest = mergeOptions(appManifest, baseSDKManifest, manifestExtension);
21 | const json = JSON.stringify(manifest, undefined, 2);
22 | const outFile = process.argv[2];
23 | fs.writeFileSync(outFile, json, {encoding: "utf8"});
24 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/search.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/scripts/sdk/test/esm-entry.ts:
--------------------------------------------------------------------------------
1 | import * as hydrogenViewSdk from "hydrogen-view-sdk";
2 | import downloadSandboxPath from 'hydrogen-view-sdk/download-sandbox.html?url';
3 | import workerPath from 'hydrogen-view-sdk/main.js?url';
4 | import olmWasmPath from '@matrix-org/olm/olm.wasm?url';
5 | import olmJsPath from '@matrix-org/olm/olm.js?url';
6 | import olmLegacyJsPath from '@matrix-org/olm/olm_legacy.js?url';
7 | const assetPaths = {
8 | downloadSandbox: downloadSandboxPath,
9 | worker: workerPath,
10 | olm: {
11 | wasm: olmWasmPath,
12 | legacyBundle: olmLegacyJsPath,
13 | wasmBundle: olmJsPath
14 | }
15 | };
16 | import "hydrogen-view-sdk/assets/theme-element-light.css";
17 |
18 | console.log('hydrogenViewSdk', hydrogenViewSdk);
19 | console.log('assetPaths', assetPaths);
20 |
21 | console.log('Entry ESM works ✅');
22 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true,
5 | },
6 | extends: "eslint:recommended",
7 | parserOptions: {
8 | ecmaVersion: 2020,
9 | sourceType: "module",
10 | },
11 | rules: {
12 | "no-console": "off",
13 | "no-empty": "off",
14 | "no-prototype-builtins": "off",
15 | "no-unused-vars": "warn",
16 | },
17 | globals: {
18 | DEFINE_VERSION: "readonly",
19 | DEFINE_GLOBAL_HASH: "readonly",
20 | DEFINE_IS_SDK: "readonly",
21 | DEFINE_PROJECT_DIR: "readonly",
22 | // only available in sw.js
23 | DEFINE_UNHASHED_PRECACHED_ASSETS: "readonly",
24 | DEFINE_HASHED_PRECACHED_ASSETS: "readonly",
25 | DEFINE_HASHED_CACHED_ON_REQUEST_ASSETS: "readonly",
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/room/timeline/AnnouncementView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {TemplateView} from "../../../general/TemplateView";
10 |
11 | export class AnnouncementView extends TemplateView {
12 | // ignore other arguments
13 | constructor(vm) {
14 | super(vm);
15 | }
16 |
17 | render(t, vm) {
18 | return t.li({
19 | className: "AnnouncementView",
20 | 'data-event-id': vm.eventId
21 | }, t.div(vm => vm.announcement));
22 | }
23 |
24 | /* This is called by the parent ListView, which just has 1 listener for the whole list */
25 | onClick() {}
26 | }
27 |
--------------------------------------------------------------------------------
/src/utils/formatSize.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 |
10 | export function formatSize(size: number, decimals: number = 2): string {
11 | if (Number.isSafeInteger(size)) {
12 | const base = Math.min(3, Math.floor(Math.log(size) / Math.log(1024)));
13 | const formattedSize = Math.round(size / Math.pow(1024, base)).toFixed(decimals);
14 | switch (base) {
15 | case 0: return `${formattedSize} bytes`;
16 | case 1: return `${formattedSize} KB`;
17 | case 2: return `${formattedSize} MB`;
18 | case 3: return `${formattedSize} GB`;
19 | }
20 | }
21 | return "";
22 | }
23 |
--------------------------------------------------------------------------------
/prototypes/base256.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/platform/web/ui/general/StaticView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 | Copyright 2020 The Matrix.org Foundation C.I.C.
5 |
6 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
7 | Please see LICENSE files in the repository root for full details.
8 | */
9 |
10 | import {tag} from "../general/html";
11 |
12 | export class StaticView {
13 | constructor(value, render = undefined) {
14 | if (typeof value === "function" && !render) {
15 | render = value;
16 | value = null;
17 | }
18 | this._root = render ? render(tag, value) : this.render(tag, value);
19 | }
20 |
21 | mount() {
22 | return this._root;
23 | }
24 |
25 | root() {
26 | return this._root;
27 | }
28 |
29 | unmount() {}
30 | update() {}
31 | }
32 |
--------------------------------------------------------------------------------
/src/matrix/verification/SAS/stages/SendDoneStage.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import {BaseSASVerificationStage} from "./BaseSASVerificationStage";
9 | import {VerificationEventType} from "../channel/types";
10 |
11 | export class SendDoneStage extends BaseSASVerificationStage {
12 | async completeStage() {
13 | await this.log.wrap("SendDoneStage.completeStage", async (log) => {
14 | await this.channel.send(VerificationEventType.Done, {}, log);
15 | await this.channel.waitForEvent(VerificationEventType.Done);
16 | this.eventEmitter.emit("VerificationCompleted", this.otherUserDeviceId);
17 | });
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/room/timeline/FileView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {BaseMessageView} from "./BaseMessageView.js";
10 |
11 | export class FileView extends BaseMessageView {
12 | renderMessageBody(t, vm) {
13 | const children = [];
14 | if (vm.isPending) {
15 | children.push(vm => vm.label);
16 | } else {
17 | children.push(
18 | t.button({className: "link", onClick: () => vm.download()}, vm => vm.label),
19 | t.time(vm.time)
20 | );
21 | }
22 | return t.p({className: "Timeline_messageBody statusMessage"}, children);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/room/timeline/RedactedView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {BaseMessageView} from "./BaseMessageView.js";
10 | import {Menu} from "../../../general/Menu.js";
11 |
12 | export class RedactedView extends BaseMessageView {
13 | renderMessageBody(t) {
14 | return t.p({className: "Timeline_messageBody statusMessage"}, vm => vm.description);
15 | }
16 |
17 | createMenuOptions(vm) {
18 | const options = super.createMenuOptions(vm);
19 | if (vm.isRedacting) {
20 | options.push(Menu.option(vm.i18n`Cancel`, () => vm.abortPendingRedaction()));
21 | }
22 | return options;
23 | }
24 | }
--------------------------------------------------------------------------------
/scripts/sdk/base-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hydrogen-view-sdk",
3 | "description": "Embeddable matrix client library, including view components",
4 | "version": "0.3.1",
5 | "main": "./lib-build/hydrogen.cjs.js",
6 | "exports": {
7 | ".": {
8 | "import": "./lib-build/hydrogen.es.js",
9 | "require": "./lib-build/hydrogen.cjs.js"
10 | },
11 | "./paths/vite": "./paths/vite.js",
12 | "./style.css": "./asset-build/assets/theme-element-light.css",
13 | "./theme-element-light.css": "./asset-build/assets/theme-element-light.css",
14 | "./theme-element-dark.css": "./asset-build/assets/theme-element-dark.css",
15 | "./main.js": "./asset-build/assets/main.js",
16 | "./download-sandbox.html": "./asset-build/assets/download-sandbox.html",
17 | "./assets/*": "./asset-build/assets/*"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/doc/implementation planning/CATCHUP-BACKFILL.md:
--------------------------------------------------------------------------------
1 | we should automatically fill gaps (capped at a certain (large) amount of events, 5000?) after a limited sync for a room
2 |
3 | ## E2EE rooms
4 |
5 | during these fills (once supported), we should calculate push actions and trigger notifications, as we would otherwise have received this through sync.
6 |
7 | we could also trigger notifications when just backfilling on initial sync up to a certain amount of time in the past?
8 |
9 |
10 | we also need to backfill if we didn't receive any m.room.message in a limited sync for an encrypted room, as it's possible the room summary hasn't seen the last message in the room and is now out of date. this is also true for a non-encrypted room actually, although wrt to the above, here notifications would work well though.
11 |
12 | a room should request backfills in needsAfterSyncCompleted and do them in afterSyncCompleted.
13 |
--------------------------------------------------------------------------------
/prototypes/online.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/matrix/storage/idb/stores/UserIdentityStore.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import {Store} from "../Store";
9 | import type {UserIdentity} from "../../../e2ee/DeviceTracker";
10 |
11 | export class UserIdentityStore {
12 | private _store: Store;
13 |
14 | constructor(store: Store) {
15 | this._store = store;
16 | }
17 |
18 | get(userId: string): Promise {
19 | return this._store.get(userId);
20 | }
21 |
22 | set(userIdentity: UserIdentity): void {
23 | this._store.put(userIdentity);
24 | }
25 |
26 | remove(userId: string): void {
27 | this._store.delete(userId);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/prototypes/ie11-textdecoder.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/matrix/e2ee/megolm/decryption/README.md:
--------------------------------------------------------------------------------
1 | Lots of classes here. The complexity comes from needing to offload decryption to a webworker, mainly for IE11. We can't keep a idb transaction open while waiting for the response from the worker, so need to batch decryption of multiple events and do decryption in multiple steps:
2 |
3 | 1. Read all used inbound sessions for the batch of events, requires a read txn. This happens in `Decryption`. Sessions are loaded into `SessionInfo` objects, which are also kept in a `SessionCache` to prevent having to read and unpickle them all the time.
4 | 2. Actually decrypt. No txn can stay open during this step, as it can be offloaded to a worker and is thus async. This happens in `DecryptionPreparation`, which delegates to `SessionDecryption` per session.
5 | 3. Read and write for the replay detection, requires a read/write txn. This happens in `DecryptionChanges`
6 | 4. Return the decrypted entries, and errors if any
7 |
--------------------------------------------------------------------------------
/.ts-eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | "browser": true,
5 | "es6": true
6 | },
7 | extends: [
8 | // "plugin:@typescript-eslint/recommended",
9 | // "plugin:@typescript-eslint/recommended-requiring-type-checking",
10 | ],
11 | parser: '@typescript-eslint/parser',
12 | parserOptions: {
13 | "ecmaVersion": 2020,
14 | "sourceType": "module",
15 | "project": "./tsconfig.json"
16 | },
17 | plugins: [
18 | '@typescript-eslint',
19 | ],
20 | rules: {
21 | "@typescript-eslint/no-floating-promises": 2,
22 | "@typescript-eslint/no-misused-promises": 2,
23 | "no-unused-vars": "off",
24 | "@typescript-eslint/no-unused-vars": ["warn"],
25 | "no-undef": "off",
26 | "semi": ["error", "always"],
27 | "@typescript-eslint/explicit-function-return-type": ["error"]
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/src/platform/web/ui/general/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 | Copyright 2021 Daniel Fedorin
5 |
6 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
7 | Please see LICENSE files in the repository root for full details.
8 | */
9 | export interface IMountArgs {
10 | // if true, the parent will call update() rather than the view updating itself by binding to a data source.
11 | parentProvidesUpdates?: boolean
12 | };
13 |
14 | // Comment nodes can be used as temporary placeholders for Elements, like TemplateView does.
15 | export type ViewNode = Element | Comment;
16 |
17 | export interface IView {
18 | mount(args?: IMountArgs): ViewNode;
19 | root(): ViewNode | undefined; // should only be called between mount() and unmount()
20 | unmount(): void;
21 | update(...any); // this isn't really standarized yet
22 | }
23 |
--------------------------------------------------------------------------------
/src/domain/session/room/README.md:
--------------------------------------------------------------------------------
1 | # "Room" view models
2 |
3 | InviteViewModel, RoomViewModel and RoomBeingCreatedViewModel are interchangebly used as "room view model":
4 | - SessionViewModel.roomViewModel can be an instance of any
5 | - RoomGridViewModel.roomViewModelAt(i) can return an instance of any
6 |
7 | This is because they are accessed by the same url and need to transition into each other, in these two locations. Having two methods, especially in RoomGridViewModel would have been more cumbersome, even though this is not in line with how different view models are exposed in SessionViewModel.
8 |
9 | They share an `id` and `kind` property, the latter can be used to differentiate them from the view, and a `focus` method.
10 | Once we convert this folder to typescript, we should use this interface for all the view models:
11 | ```ts
12 | interface IGridItemViewModel {
13 | id: string;
14 | kind: string;
15 | focus();
16 | }
17 | ```
18 |
--------------------------------------------------------------------------------
/src/domain/session/room/timeline/tiles/MissingAttachmentTile.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {BaseMessageTile} from "./BaseMessageTile.js";
10 |
11 | export class MissingAttachmentTile extends BaseMessageTile {
12 | get shape() {
13 | return "missing-attachment"
14 | }
15 |
16 | get label() {
17 | const name = this._getContent().body;
18 | const msgtype = this._getContent().msgtype;
19 | if (msgtype === "m.image") {
20 | return this.i18n`The image ${name} wasn't fully sent previously and could not be recovered.`;
21 | } else {
22 | return this.i18n`The file ${name} wasn't fully sent previously and could not be recovered.`;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/platform/web/ui/login/SessionLoadView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {TemplateView} from "../general/TemplateView";
10 | import {SessionLoadStatusView} from "./SessionLoadStatusView.js";
11 |
12 | export class SessionLoadView extends TemplateView {
13 | render(t, vm) {
14 | return t.div({className: "PreSessionScreen"}, [
15 | t.div({className: "logo"}),
16 | t.div({className: "SessionLoadView"}, [
17 | t.view(new SessionLoadStatusView(vm))
18 | ]),
19 | t.div({className: {"button-row": true, hidden: vm => vm.loading}},
20 | t.a({className: "button-action primary", href: vm.backUrl}, vm.i18n`Go back`))
21 | ]);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/matrix/e2ee/megolm/decryption/ReplayDetectionEntry.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import type {TimelineEvent} from "../../../storage/types";
10 |
11 | export class ReplayDetectionEntry {
12 | public readonly sessionId: string;
13 | public readonly messageIndex: number;
14 | public readonly event: TimelineEvent;
15 |
16 | constructor(sessionId: string, messageIndex: number, event: TimelineEvent) {
17 | this.sessionId = sessionId;
18 | this.messageIndex = messageIndex;
19 | this.event = event;
20 | }
21 |
22 | get eventId(): string {
23 | return this.event.event_id;
24 | }
25 |
26 | get timestamp(): number {
27 | return this.event.origin_server_ts;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/matrix/room/timeline/persistence/common.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export function createEventEntry(key, roomId, event) {
10 | return {
11 | fragmentId: key.fragmentId,
12 | eventIndex: key.eventIndex,
13 | roomId,
14 | event: event,
15 | };
16 | }
17 |
18 | export function directionalAppend(array, value, direction) {
19 | if (direction.isForward) {
20 | array.push(value);
21 | } else {
22 | array.unshift(value);
23 | }
24 | }
25 |
26 | export function directionalConcat(array, otherArray, direction) {
27 | if (direction.isForward) {
28 | return array.concat(otherArray);
29 | } else {
30 | return otherArray.concat(array);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/cam-muted.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/matrix/room/timeline/Direction.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export class Direction {
10 | constructor(public readonly isForward: boolean) {
11 | }
12 |
13 | get isBackward(): boolean {
14 | return !this.isForward;
15 | }
16 |
17 | asApiString(): string {
18 | return this.isForward ? "f" : "b";
19 | }
20 |
21 | reverse(): Direction {
22 | return this.isForward ? Direction.Backward : Direction.Forward
23 | }
24 |
25 | static get Forward(): Direction {
26 | return _forward;
27 | }
28 |
29 | static get Backward(): Direction {
30 | return _backward;
31 | }
32 | }
33 |
34 | const _forward = new Direction(true);
35 | const _backward = new Direction(false);
36 |
--------------------------------------------------------------------------------
/src/platform/web/ui/common.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | let container;
10 |
11 | export function spinner(t, extraClasses = undefined) {
12 | if (container === undefined) {
13 | container = document.querySelector(".hydrogen");
14 | }
15 | const classes = Object.assign({"spinner": true}, extraClasses);
16 | if (container?.classList.contains("legacy")) {
17 | return t.div({className: classes}, [
18 | t.div(),
19 | t.div(),
20 | t.div(),
21 | t.div(),
22 | ]);
23 | } else {
24 | return t.svg({className: classes, viewBox:"0 0 100 100"},
25 | t.circle({cx:"50%", cy:"50%", r:"45%", pathLength:"100"})
26 | );
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/doc/implementation planning/LOCAL-ECHO-STATE.md:
--------------------------------------------------------------------------------
1 | # Local echo
2 |
3 | ## Remote vs local state for account_data, etc ...
4 |
5 | For things like account data, and other requests that might fail, we could persist what we are sending next to the last remote version we have (with a flag for which one is remote and local, part of the key). E.g. for account data the key would be: [type, localOrRemoteFlag]
6 |
7 | localOrRemoteFlag would be 1 of 3:
8 | - Remote
9 | - (Local)Unsent
10 | - (Local)Sent
11 |
12 | although we only want 1 remote and 1 local value for a given key, perhaps a second field where localOrRemoteFlag is a boolean, and a sent=boolean field as well? We need this to know if we need to retry.
13 |
14 | This will allow resending of these requests if needed. Once the request goes through, we remove the local version.
15 |
16 | then we can also see what the current value is with or without the pending local changes, and we don't have to wait for remote echo...
17 |
--------------------------------------------------------------------------------
/src/platform/web/ui/login/CompleteSSOView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {TemplateView} from "../general/TemplateView";
10 | import {SessionLoadStatusView} from "./SessionLoadStatusView.js";
11 |
12 | export class CompleteSSOView extends TemplateView {
13 | render(t) {
14 | return t.div({ className: "CompleteSSOView" },
15 | [
16 | t.p({ className: "CompleteSSOView_title" }, "Finishing up your SSO Login"),
17 | t.if(vm => vm.errorMessage, (t, vm) => t.p({className: "error"}, vm.i18n(vm.errorMessage))),
18 | t.mapView(vm => vm.loadViewModel, loadViewModel => loadViewModel ? new SessionLoadStatusView(loadViewModel) : null),
19 | ]
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/utils/Deferred.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export class Deferred {
10 | public readonly promise: Promise;
11 | public readonly resolve: (value: T) => void;
12 | public readonly reject: (err: Error) => void;
13 | private _value?: T;
14 |
15 | constructor() {
16 | let resolve;
17 | let reject;
18 | this.promise = new Promise((_resolve, _reject) => {
19 | resolve = _resolve;
20 | reject = _reject;
21 | })
22 | this.resolve = (value: T) => {
23 | this._value = value;
24 | resolve(value);
25 | };
26 | this.reject = reject;
27 | }
28 |
29 | get value(): T | undefined {
30 | return this._value;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/platform/web/assets/icon.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/logging/utils.ts:
--------------------------------------------------------------------------------
1 | // these are helper functions if you can't assume you always have a log item (e.g. some code paths call with one set, others don't)
2 | // if you know you always have a log item, better to use the methods on the log item than these utility functions.
3 |
4 | import {Instance as NullLoggerInstance} from "./NullLogger";
5 | import type {FilterCreator, ILogItem, LabelOrValues, LogCallback} from "./types";
6 | import {LogLevel} from "./LogFilter";
7 |
8 | export function wrapOrRunNullLogger(logItem: ILogItem | undefined, labelOrValues: LabelOrValues, callback: LogCallback, logLevel?: LogLevel, filterCreator?: FilterCreator): T | Promise {
9 | if (logItem) {
10 | return logItem.wrap(labelOrValues, callback, logLevel, filterCreator);
11 | } else {
12 | return NullLoggerInstance.run(null, callback);
13 | }
14 | }
15 |
16 | export function ensureLogItem(logItem: ILogItem): ILogItem {
17 | return logItem || NullLoggerInstance.item;
18 | }
19 |
--------------------------------------------------------------------------------
/src/matrix/storage/idb/stores/OutboundGroupSessionStore.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import {Store} from "../Store";
9 |
10 | interface OutboundSession {
11 | roomId: string;
12 | session: string;
13 | createdAt: number;
14 | }
15 |
16 | export class OutboundGroupSessionStore {
17 | private _store: Store;
18 |
19 | constructor(store: Store) {
20 | this._store = store;
21 | }
22 |
23 | remove(roomId: string): void {
24 | this._store.delete(roomId);
25 | }
26 |
27 | get(roomId: string): Promise {
28 | return this._store.get(roomId);
29 | }
30 |
31 | set(session: OutboundSession): void {
32 | this._store.put(session);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/matrix/storage/idb/stores/SharedSecretStore.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import {Store} from "../Store";
9 |
10 | type SharedSecret = any;
11 |
12 | export class SharedSecretStore {
13 | private _store: Store;
14 |
15 | constructor(store: Store) {
16 | this._store = store;
17 | }
18 |
19 | get(name: string): Promise {
20 | return this._store.get(name);
21 | }
22 |
23 | set(name: string, secret: SharedSecret): void {
24 | secret.key = name;
25 | this._store.put(secret);
26 | }
27 |
28 | remove(name: string): void {
29 | this._store.delete(name);
30 | }
31 |
32 | deleteAllSecrets(): void {
33 | this._store.clear();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/doc/implementation planning/ROOM-VERSIONS.md:
--------------------------------------------------------------------------------
1 | - add internal room ids (to support room versioning later, and make internal event ids smaller and not needing escaping, and not needing a migration later on) ... hm this might need some more though. how to address a logical room? last room id? also we might not need it for room versioning ... it would basically be to make the ids smaller, but as idb is compressing, not sure that's a good reason? Although as we keep all room summaries in memory, it would be easy to map between these... you'd get event ids like 0000E78A00000020000A0B3C with room id, fragment id and event index. The room summary would store:
2 | ```
3 | rooms: {
4 | "!eKhOsgLidcrWMWnxOr:vector.modular.im": 0x0000E78A,
5 | ...
6 | }
7 | mostRecentRoom: 0x0000E78A
8 | ```
9 | if this is not on an indexed field, how can we do a query to find the last room id and +1 to assign a new one?
10 |
11 | how do we identify a logical room (consisting on a recent room and perhaps multiple outdated ones)?
12 |
--------------------------------------------------------------------------------
/src/matrix/verification/SAS/channel/types.ts:
--------------------------------------------------------------------------------
1 | export const enum VerificationEventType {
2 | Request = "m.key.verification.request",
3 | Ready = "m.key.verification.ready",
4 | Start = "m.key.verification.start",
5 | Accept = "m.key.verification.accept",
6 | Key = "m.key.verification.key",
7 | Cancel = "m.key.verification.cancel",
8 | Mac = "m.key.verification.mac",
9 | Done = "m.key.verification.done",
10 | }
11 |
12 | export const enum CancelReason {
13 | UserCancelled = "m.user",
14 | TimedOut = "m.timeout",
15 | UnknownTransaction = "m.unknown_transaction",
16 | UnknownMethod = "m.unknown_method",
17 | UnexpectedMessage = "m.unexpected_message",
18 | KeyMismatch = "m.key_mismatch",
19 | UserMismatch = "m.user_mismatch",
20 | InvalidMessage = "m.invalid_message",
21 | OtherDeviceAccepted = "m.accepted",
22 | // SAS specific
23 | MismatchedCommitment = "m.mismatched_commitment",
24 | MismatchedSAS = "m.mismatched_sas",
25 | }
26 |
--------------------------------------------------------------------------------
/src/matrix/room/members/MemberList.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {ObservableMap} from "../../../observable";
10 | import {RetainedValue} from "../../../utils/RetainedValue";
11 |
12 | export class MemberList extends RetainedValue {
13 | constructor({members, closeCallback}) {
14 | super(closeCallback);
15 | this._members = new ObservableMap();
16 | for (const member of members) {
17 | this._members.add(member.userId, member);
18 | }
19 | }
20 |
21 | afterSync(memberChanges) {
22 | for (const [userId, memberChange] of memberChanges.entries()) {
23 | this._members.set(userId, memberChange.member);
24 | }
25 | }
26 |
27 | get members() {
28 | return this._members;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/observable/map/index.ts:
--------------------------------------------------------------------------------
1 | // In order to avoid a circular dependency problem at runtime between BaseObservableMap
2 | // and the classes that extend it, it's important that:
3 | //
4 | // 1) It always remain the first module exported below.
5 | // 2) Anything that imports any of the classes in this module
6 | // ONLY import them from this index.ts file.
7 | //
8 | // See https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de
9 | // for more on why this discipline is necessary.
10 | export {BaseObservableMap} from './BaseObservableMap';
11 | export type {Mapper, Updater, Comparator, Filter} from './BaseObservableMap';
12 | export {ApplyMap} from './ApplyMap';
13 | export {FilteredMap} from './FilteredMap';
14 | export {JoinedMap} from './JoinedMap';
15 | export {LogMap} from './LogMap';
16 | export {MappedMap} from './MappedMap';
17 | export {ObservableMap} from './ObservableMap';
18 | export {ObservableValueMap} from './ObservableValueMap';
19 |
--------------------------------------------------------------------------------
/src/observable/value/index.ts:
--------------------------------------------------------------------------------
1 | // In order to avoid a circular dependency problem at runtime between BaseObservableValue
2 | // and the classes that extend it, it's important that:
3 | //
4 | // 1) It always remain the first module exported below.
5 | // 2) Anything that imports any of the classes in this module
6 | // ONLY import them from this index.ts file.
7 | //
8 | // See https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de
9 | // for more on why this discipline is necessary.
10 | export {BaseObservableValue} from './BaseObservableValue';
11 | export {EventObservableValue} from './EventObservableValue';
12 | export {FlatMapObservableValue} from './FlatMapObservableValue';
13 | export {PickMapObservableValue} from './PickMapObservableValue';
14 | export {RetainedObservableValue} from './RetainedObservableValue';
15 | export {MapSizeObservableValue} from './MapSizeObservableValue';
16 | export {ObservableValue} from './ObservableValue';
17 |
--------------------------------------------------------------------------------
/src/matrix/registration/stages/TermsAuth.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {AuthenticationData} from "../types";
10 | import {BaseRegistrationStage} from "./BaseRegistrationStage";
11 |
12 | export class TermsAuth extends BaseRegistrationStage {
13 | generateAuthenticationData(): AuthenticationData {
14 | return {
15 | session: this._session,
16 | type: this.type,
17 | // No other auth data needed for m.login.terms
18 | };
19 | }
20 |
21 | get type(): string {
22 | return "m.login.terms";
23 | }
24 |
25 | get privacyPolicy() {
26 | return this._params?.policies["privacy_policy"];
27 | }
28 |
29 | get termsOfService() {
30 | return this._params?.policies["terms_of_service"];
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/doc/architecture/sync-updates.md:
--------------------------------------------------------------------------------
1 | # persistance vs model update of a room
2 |
3 | ## persist first, return update object, update model with update object
4 | - we went with this
5 | ## update model first, return update object, persist with update object
6 | - not all models exist at all times (timeline only when room is "open"),
7 | so model to create timeline update object might not exist for persistence need
8 |
9 | ## persist, update, each only based on sync data (independent of each other)
10 | - possible inconsistency between syncing and loading from storage as they are different code paths
11 | + storage code remains very simple and focussed
12 |
13 | ## updating model and persisting in one go
14 | - if updating model needs to do anything async, it needs to postpone it or the txn will be closed
15 |
16 | ## persist first, read from storage to update model
17 | + guaranteed consistency between what is on screen and in storage
18 | - slower as we need to reread what was just synced every time (big accounts with frequent updates)
19 |
--------------------------------------------------------------------------------
/src/matrix/storage/idb/stores/AccountDataStore.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import {Store} from "../Store";
9 | import {Content} from "../../types";
10 |
11 | export interface AccountDataEntry {
12 | type: string;
13 | content: Content;
14 | }
15 |
16 | export class AccountDataStore {
17 | private _store: Store;
18 |
19 | constructor(store: Store) {
20 | this._store = store;
21 | }
22 |
23 | async get(type: string): Promise {
24 | return await this._store.get(type);
25 | }
26 |
27 | set(event: AccountDataEntry): void {
28 | this._store.put(event);
29 | }
30 |
31 | async getAll(): Promise> {
32 | return await this._store.selectAll();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/matrix/verification/SAS/mac.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import type {ILogItem} from "../../../logging/types";
9 | import type {MacMethod} from "./stages/constants";
10 |
11 | const macMethods: Record = {
12 | "hkdf-hmac-sha256": "calculate_mac",
13 | "org.matrix.msc3783.hkdf-hmac-sha256": "calculate_mac_fixed_base64",
14 | "hkdf-hmac-sha256.v2": "calculate_mac_fixed_base64",
15 | "hmac-sha256": "calculate_mac_long_kdf",
16 | };
17 |
18 | export function createCalculateMAC(olmSAS: Olm.SAS, method: MacMethod) {
19 | return function (input: string, info: string, log: ILogItem): string {
20 | return log.wrap({ l: "calculate MAC", method}, () => {
21 | const mac = olmSAS[macMethods[method]](input, info);
22 | return mac;
23 | });
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/src/matrix/verification/SAS/stages/SendReadyStage.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import {BaseSASVerificationStage} from "./BaseSASVerificationStage";
9 | import {VerificationEventType} from "../channel/types";
10 | import {SelectVerificationMethodStage} from "./SelectVerificationMethodStage";
11 |
12 | export class SendReadyStage extends BaseSASVerificationStage {
13 | async completeStage() {
14 | await this.log.wrap("SendReadyStage.completeStage", async (log) => {
15 | const content = {
16 | "from_device": this.ourUserDeviceId,
17 | "methods": ["m.sas.v1"],
18 | };
19 | await this.channel.send(VerificationEventType.Ready, content, log);
20 | this.setNextStage(new SelectVerificationMethodStage(this.options));
21 | });
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/domain/session/room/timeline/tiles/ImageTile.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 | Copyright 2020 The Matrix.org Foundation C.I.C.
5 |
6 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
7 | Please see LICENSE files in the repository root for full details.
8 | */
9 |
10 | import {BaseMediaTile} from "./BaseMediaTile.js";
11 |
12 | export class ImageTile extends BaseMediaTile {
13 | constructor(entry, options) {
14 | super(entry, options);
15 | this._lightboxUrl = this.urlRouter.urlForSegments([
16 | // ensure the right room is active if in grid view
17 | this.navigation.segment("room", this._room.id),
18 | this.navigation.segment("lightbox", this._entry.id)
19 | ]);
20 | }
21 |
22 | get lightboxUrl() {
23 | if (!this.isPending) {
24 | return this._lightboxUrl;
25 | }
26 | return "";
27 | }
28 |
29 | get shape() {
30 | return "image";
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/matrix/login/TokenLoginMethod.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {makeTxnId} from "../common.js";
10 | import {ILogItem} from "../../logging/types";
11 | import {ILoginMethod} from "./LoginMethod";
12 | import {HomeServerApi} from "../net/HomeServerApi.js";
13 |
14 | export class TokenLoginMethod implements ILoginMethod {
15 | private readonly _loginToken: string;
16 | public readonly homeserver: string;
17 |
18 | constructor({ homeserver, loginToken }: { homeserver: string, loginToken: string}) {
19 | this.homeserver = homeserver;
20 | this._loginToken = loginToken;
21 | }
22 |
23 | async login(hsApi: HomeServerApi, deviceName: string, log: ILogItem): Promise> {
24 | return await hsApi.tokenLogin(this._loginToken, makeTxnId(), deviceName, {log}).response();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/utils/mergeMap.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export function mergeMap(src: Map | undefined, dst: Map): void {
10 | if (src) {
11 | for (const [key, value] of src.entries()) {
12 | dst.set(key, value);
13 | }
14 | }
15 | }
16 |
17 | export function tests() {
18 | return {
19 | "mergeMap with src": assert => {
20 | const src = new Map();
21 | src.set(1, "a");
22 | const dst = new Map();
23 | dst.set(2, "b");
24 | mergeMap(src, dst);
25 | assert.equal(dst.get(1), "a");
26 | assert.equal(dst.get(2), "b");
27 | assert.equal(src.get(2), null);
28 | },
29 | "mergeMap without src doesn't fail": () => {
30 | mergeMap(undefined, new Map());
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/hangup.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/voice-call.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/platform/web/assets/icon-maskable.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM --platform=${BUILDPLATFORM} docker.io/node:alpine as builder
2 | RUN apk add --no-cache git python3 build-base
3 |
4 | WORKDIR /app
5 |
6 | # Copy package.json and yarn.lock and install dependencies first to speed up subsequent builds
7 | COPY package.json yarn.lock /app/
8 | RUN yarn install
9 |
10 | COPY . /app
11 | RUN yarn build
12 |
13 | # Because we will be running as an unprivileged user, we need to make sure that the config file is writable
14 | # So, we will copy the default config to the /tmp folder that will be writable at runtime
15 | RUN mv -f target/config.json /config.json.bundled \
16 | && ln -sf /tmp/config.json target/config.json
17 |
18 | FROM --platform=${TARGETPLATFORM} docker.io/nginxinc/nginx-unprivileged:alpine
19 |
20 | # Copy the dynamic config script
21 | COPY ./docker/dynamic-config.sh /docker-entrypoint.d/99-dynamic-config.sh
22 | # And the bundled config file
23 | COPY --from=builder /config.json.bundled /config.json.bundled
24 |
25 | # Copy the built app from the first build stage
26 | COPY --from=builder /app/target /usr/share/nginx/html
27 |
--------------------------------------------------------------------------------
/scripts/sdk/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Exit whenever one of the commands fail with a non-zero exit code
3 | set -e
4 | set -o pipefail
5 | # Enable extended globs so we can use the `!(filename)` glob syntax
6 | shopt -s extglob
7 |
8 | # Only remove the directory contents instead of the whole directory to maintain
9 | # the `npm link`/`yarn link` symlink
10 | rm -rf target/*
11 | yarn run vite build -c vite.sdk-assets-config.js --mode sdk
12 | yarn run vite build -c vite.sdk-lib-config.js --mode sdk
13 | yarn tsc -p tsconfig-declaration.json
14 | ./scripts/sdk/create-manifest.js ./target/package.json
15 | mkdir target/paths
16 | # this doesn't work, the ?url imports need to be in the consuming project, so disable for now
17 | # ./scripts/sdk/transform-paths.js ./src/platform/web/sdk/paths/vite.js ./target/paths/vite.js
18 | cp doc/SDK.md target/README.md
19 | cp doc/SDK-CHANGELOG.md target/CHANGELOG.md
20 | pushd target/asset-build
21 | rm index.html
22 | popd
23 | pushd target/asset-build/assets
24 | # Remove all `*.wasm` and `*.js` files except for `main.js`
25 | rm !(main).js *.wasm
26 | popd
27 |
--------------------------------------------------------------------------------
/src/domain/session/toast/BaseToastNotificationViewModel.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {ErrorReportViewModel} from "../../ErrorReportViewModel";
10 | import {Options as BaseOptions} from "../../ViewModel";
11 | import type {Session} from "../../../matrix/Session.js";
12 | import {SegmentType} from "../../navigation";
13 |
14 | export type BaseClassOptions = {
15 | dismiss: () => void;
16 | session: Session;
17 | } & BaseOptions;
18 |
19 | export abstract class BaseToastNotificationViewModel = BaseClassOptions> extends ErrorReportViewModel {
20 | constructor(options: O) {
21 | super(options);
22 | }
23 |
24 | dismiss(): void {
25 | this.getOption("dismiss")();
26 | }
27 |
28 | abstract get kind(): string;
29 | }
30 |
--------------------------------------------------------------------------------
/src/matrix/verification/SAS/SASRequest.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import type {CrossSigning} from "../CrossSigning";
10 | import type {Room} from "../../room/Room.js";
11 | import type {ILogItem} from "../../../logging/types";
12 |
13 | export class SASRequest {
14 | constructor(public readonly startingMessage: any) {}
15 |
16 | get deviceId(): string {
17 | return this.startingMessage.content.from_device;
18 | }
19 |
20 | get sender(): string {
21 | return this.startingMessage.sender;
22 | }
23 |
24 | get id(): string {
25 | return this.startingMessage.content.transaction_id ?? this.startingMessage.eventId;
26 | }
27 |
28 | async reject(crossSigning: CrossSigning, room: Room, log: ILogItem): Promise {
29 | const sas = crossSigning.startVerification(this, room, log);
30 | await sas?.abort();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/left-panel.css:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | .LeftPanel {
10 | display: flex;
11 | flex-direction: column;
12 | }
13 |
14 | .LeftPanel .utilities {
15 | display: flex;
16 | }
17 |
18 | .LeftPanel .utilities .FilterField {
19 | flex: 1;
20 | min-width: 0;
21 | }
22 |
23 | .LeftPanel ul {
24 | list-style: none;
25 | padding: 0;
26 | margin: 0;
27 | }
28 |
29 | .RoomList {
30 | flex: 1 0 0;
31 | overflow-y: auto;
32 | overscroll-behavior: contain;
33 | }
34 |
35 | .RoomList > li > a {
36 | display: flex;
37 | align-items: center;
38 | }
39 |
40 | .RoomList .description {
41 | margin: 0;
42 | flex: 1 1 0;
43 | min-width: 0;
44 | display: flex;
45 | }
46 |
47 | .RoomList .description > .name {
48 | overflow: hidden;
49 | white-space: nowrap;
50 | text-overflow: ellipsis;
51 | flex: 1;
52 | }
53 |
--------------------------------------------------------------------------------
/src/matrix/e2ee/olm/types.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2022 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import type {OLM_ALGORITHM} from "../common";
10 |
11 | export const enum OlmPayloadType {
12 | PreKey = 0,
13 | Normal = 1
14 | }
15 |
16 | export type OlmMessage = {
17 | type?: OlmPayloadType,
18 | body?: string
19 | }
20 |
21 | export type OlmEncryptedMessageContent = {
22 | algorithm?: typeof OLM_ALGORITHM
23 | sender_key?: string,
24 | ciphertext?: {
25 | [deviceCurve25519Key: string]: OlmMessage
26 | }
27 | }
28 |
29 | export type OlmEncryptedEvent = {
30 | type?: "m.room.encrypted",
31 | content?: OlmEncryptedMessageContent
32 | sender?: string
33 | }
34 |
35 | export type OlmPayload = {
36 | type?: string;
37 | content?: Record;
38 | sender?: string;
39 | recipient?: string;
40 | recipient_keys?: {ed25519?: string};
41 | keys?: {ed25519?: string};
42 | }
43 |
--------------------------------------------------------------------------------
/src/matrix/login/PasswordLoginMethod.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {ILogItem} from "../../logging/types";
10 | import {ILoginMethod} from "./LoginMethod";
11 | import {HomeServerApi} from "../net/HomeServerApi.js";
12 |
13 | export class PasswordLoginMethod implements ILoginMethod {
14 | private readonly _username: string;
15 | private readonly _password: string;
16 | public readonly homeserver: string;
17 |
18 | constructor({username, password, homeserver}: {username: string, password: string, homeserver: string}) {
19 | this._username = username;
20 | this._password = password;
21 | this.homeserver = homeserver;
22 | }
23 |
24 | async login(hsApi: HomeServerApi, deviceName: string, log: ILogItem): Promise> {
25 | return await hsApi.passwordLogin(this._username, this._password, deviceName, {log}).response();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/enable-grid.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/SessionStatusView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {TemplateView} from "../general/TemplateView";
10 | import {spinner} from "../common.js";
11 |
12 | export class SessionStatusView extends TemplateView {
13 | render(t, vm) {
14 | return t.div({className: {
15 | "SessionStatusView": true,
16 | "hidden": vm => !vm.isShown,
17 | }}, [
18 | spinner(t, {hidden: vm => !vm.isWaiting}),
19 | t.p(vm => vm.statusLabel),
20 | t.if(vm => vm.isConnectNowShown, t => t.button({className: "link", onClick: () => vm.connectNow()}, "Retry now")),
21 | t.if(vm => vm.isSecretStorageShown, t => t.a({href: vm.setupKeyBackupUrl}, "Go to settings")),
22 | t.if(vm => vm.canDismiss, t => t.div({className: "end"}, t.button({className: "dismiss", onClick: () => vm.dismiss()}))),
23 | ]);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/utils/recursivelyAssign.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2019 The Matrix.org Foundation C.I.C.
4 | Copyright 2015, 2016 OpenMarket Ltd
5 |
6 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
7 | Please see LICENSE files in the repository root for full details.
8 | */
9 |
10 |
11 | /**
12 | * This function is similar to Object.assign() but it assigns recursively and
13 | * allows you to ignore nullish values from the source
14 | *
15 | * @param {Object} target
16 | * @param {Object} source
17 | * @returns the target object
18 | */
19 | export function recursivelyAssign(target: Object, source: Object, ignoreNullish = false): any {
20 | for (const [sourceKey, sourceValue] of Object.entries(source)) {
21 | if (target[sourceKey] instanceof Object && sourceValue) {
22 | recursivelyAssign(target[sourceKey], sourceValue);
23 | continue;
24 | }
25 | if ((sourceValue !== null && sourceValue !== undefined) || !ignoreNullish) {
26 | target[sourceKey] = sourceValue;
27 | continue;
28 | }
29 | }
30 | return target;
31 | }
--------------------------------------------------------------------------------
/src/observable/value/EventObservableValue.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2022 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {BaseObservableValue} from "./index";
10 | import {EventEmitter} from "../../utils/EventEmitter";
11 |
12 | export class EventObservableValue> extends BaseObservableValue {
13 | private eventSubscription: () => void;
14 |
15 | constructor(
16 | private readonly value: V,
17 | private readonly eventName: keyof T
18 | ) {
19 | super();
20 | }
21 |
22 | onSubscribeFirst(): void {
23 | this.eventSubscription = this.value.disposableOn(this.eventName, () => {
24 | this.emit(this.value);
25 | });
26 | super.onSubscribeFirst();
27 | }
28 |
29 | onUnsubscribeLast(): void {
30 | this.eventSubscription!();
31 | super.onUnsubscribeLast();
32 | }
33 |
34 | get(): V {
35 | return this.value;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/observable/list/common.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 | Copyright 2020 Bruno Windels
5 |
6 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
7 | Please see LICENSE files in the repository root for full details.
8 | */
9 | import {BaseObservableList} from "./BaseObservableList";
10 |
11 | /* inline update of item in collection backed by array, without replacing the preexising item */
12 | export function findAndUpdateInArray(
13 | predicate: (value: T) => boolean,
14 | array: T[],
15 | observable: BaseObservableList,
16 | updater: (value: T) => any | false
17 | ): boolean {
18 | const index = array.findIndex(predicate);
19 | if (index !== -1) {
20 | const value = array[index];
21 | // allow bailing out of sending an emit if updater determined its not needed
22 | const params = updater(value);
23 | if (params !== false) {
24 | observable.emitUpdate(index, value, params);
25 | }
26 | // found
27 | return true;
28 | }
29 | return false;
30 | }
31 |
--------------------------------------------------------------------------------
/src/platform/web/dom/OnlineStatus.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {BaseObservableValue} from "../../../observable/value";
10 |
11 | export class OnlineStatus extends BaseObservableValue {
12 | constructor() {
13 | super();
14 | this._onOffline = this._onOffline.bind(this);
15 | this._onOnline = this._onOnline.bind(this);
16 | }
17 |
18 | _onOffline() {
19 | this.emit(false);
20 | }
21 |
22 | _onOnline() {
23 | this.emit(true);
24 | }
25 |
26 | get() {
27 | return navigator.onLine;
28 | }
29 |
30 | onSubscribeFirst() {
31 | window.addEventListener('offline', this._onOffline);
32 | window.addEventListener('online', this._onOnline);
33 | }
34 |
35 | onUnsubscribeLast() {
36 | window.removeEventListener('offline', this._onOffline);
37 | window.removeEventListener('online', this._onOnline);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/matrix/verification/SAS/stages/SendRequestVerificationStage.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import {BaseSASVerificationStage} from "./BaseSASVerificationStage";
9 | import {SelectVerificationMethodStage} from "./SelectVerificationMethodStage";
10 | import {VerificationEventType} from "../channel/types";
11 |
12 | export class SendRequestVerificationStage extends BaseSASVerificationStage {
13 | async completeStage() {
14 | await this.log.wrap("SendRequestVerificationStage.completeStage", async (log) => {
15 | const content = {
16 | "from_device": this.ourUserDeviceId,
17 | "methods": ["m.sas.v1"],
18 | };
19 | await this.channel.send(VerificationEventType.Request, content, log);
20 | this.setNextStage(new SelectVerificationMethodStage(this.options));
21 | await this.channel.waitForEvent(VerificationEventType.Ready);
22 | });
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/mocks/Request.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {AbortError} from "../utils/error";
10 |
11 | export class BaseRequest {
12 | constructor() {
13 | this._responsePromise = new Promise((resolve, reject) => {
14 | this.resolve = resolve;
15 | this.reject = reject;
16 | });
17 | this.responded = false;
18 | this.aborted = false;
19 | }
20 |
21 | _respond(value) {
22 | this.responded = true;
23 | this.resolve(value);
24 | return this;
25 | }
26 |
27 | abort() {
28 | this.aborted = true;
29 | this.reject(new AbortError());
30 | }
31 |
32 | response() {
33 | return this._responsePromise;
34 | }
35 | }
36 |
37 | // this is a NetworkRequest as used by HomeServerApi
38 | export class Request extends BaseRequest {
39 | respond(status, body) {
40 | return this._respond({status, body});
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/utils/sortedIndex.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | /**
10 | * @license
11 | * Based off baseSortedIndex function in Lodash
12 | * Copyright JS Foundation and other contributors
13 | * Released under MIT license
14 | * Based on Underscore.js 1.8.3
15 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
16 | */
17 | export function sortedIndex(array: T[], value: T, comparator: (x:T, y:T) => number): number {
18 | let low = 0;
19 | let high = array.length;
20 |
21 | while (low < high) {
22 | let mid = (low + high) >>> 1;
23 | let cmpResult = comparator(value, array[mid]);
24 |
25 | if (cmpResult > 0) {
26 | low = mid + 1;
27 | } else if (cmpResult < 0) {
28 | high = mid;
29 | } else {
30 | low = high = mid;
31 | }
32 | }
33 | return high;
34 | }
35 |
--------------------------------------------------------------------------------
/src/domain/session/room/timeline/tiles/VideoTile.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 | Copyright 2020 The Matrix.org Foundation C.I.C.
5 |
6 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
7 | Please see LICENSE files in the repository root for full details.
8 | */
9 |
10 | import {BaseMediaTile} from "./BaseMediaTile.js";
11 |
12 | export class VideoTile extends BaseMediaTile {
13 | async loadVideo() {
14 | const file = this._getContent().file;
15 | if (file && !this._decryptedFile) {
16 | this._decryptedFile = await this._loadEncryptedFile(file);
17 | this.emitChange("videoUrl");
18 | }
19 | }
20 |
21 | get videoUrl() {
22 | if (this._decryptedFile) {
23 | return this._decryptedFile.url;
24 | }
25 | const mxcUrl = this._getContent()?.url;
26 | if (typeof mxcUrl === "string") {
27 | return this._mediaRepository.mxcUrl(mxcUrl);
28 | }
29 | return "";
30 | }
31 |
32 | get shape() {
33 | return "video";
34 | }
35 |
36 | _isMainResourceImage() {
37 | return false;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/matrix/net/types/response.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export type Attachment = {
10 | body: string;
11 | info: AttachmentInfo;
12 | msgtype: string;
13 | url?: string;
14 | file?: EncryptedFile;
15 | filename?: string;
16 | }
17 |
18 | export type EncryptedFile = {
19 | key: JsonWebKey;
20 | iv: string;
21 | hashes: {
22 | sha256: string;
23 | };
24 | url: string;
25 | v: string;
26 | mimetype?: string;
27 | }
28 |
29 | type AttachmentInfo = {
30 | h?: number;
31 | w?: number;
32 | mimetype: string;
33 | size: number;
34 | duration?: number;
35 | thumbnail_url?: string;
36 | thumbnail_file?: EncryptedFile;
37 | thumbnail_info?: ThumbnailInfo;
38 | }
39 |
40 | type ThumbnailInfo = {
41 | h: number;
42 | w: number;
43 | mimetype: string;
44 | size: number;
45 | }
46 |
47 | export type VersionResponse = {
48 | versions: string[];
49 | unstable_features?: Record;
50 | }
51 |
--------------------------------------------------------------------------------
/src/platform/web/ui/login/common.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export function hydrogenGithubLink(t) {
10 | if (DEFINE_VERSION === "develop") {
11 | return t.a(
12 | {
13 | target: "_blank",
14 | href: `https://github.com/vector-im/hydrogen-web`,
15 | },
16 | `Hydrogen develop, ${DEFINE_GLOBAL_HASH}`
17 | );
18 | } else if (DEFINE_VERSION && DEFINE_GLOBAL_HASH) {
19 | return t.a(
20 | {
21 | target: "_blank",
22 | href: `https://github.com/vector-im/hydrogen-web/releases/tag/v${DEFINE_VERSION}`,
23 | },
24 | `Hydrogen v${DEFINE_VERSION} (${DEFINE_GLOBAL_HASH}) on Github`
25 | );
26 | } else {
27 | return t.a(
28 | {
29 | target: "_blank",
30 | href: "https://github.com/vector-im/hydrogen-web",
31 | },
32 | "Hydrogen on Github"
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/matrix/room/timeline/entries/NonPersistedEventEntry.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {EventEntry} from "./EventEntry.js";
10 |
11 | // EventEntry but without the two properties that are populated via SyncWriter
12 | // Useful if you want to create an EventEntry that is ephemeral
13 |
14 | export class NonPersistedEventEntry extends EventEntry {
15 | get fragmentId() {
16 | throw new Error("Cannot access fragmentId for non-persisted EventEntry");
17 | }
18 |
19 | get entryIndex() {
20 | throw new Error("Cannot access entryIndex for non-persisted EventEntry");
21 | }
22 |
23 | get isNonPersisted() {
24 | return true;
25 | }
26 |
27 | // overridden here because we reuse addLocalRelation() for updating this entry
28 | // we don't want the RedactedTile created using this entry to ever show "is being redacted"
29 | get isRedacting() {
30 | return false;
31 | }
32 |
33 | get isRedacted() {
34 | return super.isRedacting;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/matrix/registration/stages/TokenAuth.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2022 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {AuthenticationData, RegistrationParams} from "../types";
10 | import {BaseRegistrationStage} from "./BaseRegistrationStage";
11 |
12 | export class TokenAuth extends BaseRegistrationStage {
13 | private _token?: string;
14 | private readonly _type: string;
15 |
16 | constructor(session: string, params: RegistrationParams | undefined, type: string) {
17 | super(session, params);
18 | this._type = type;
19 | }
20 |
21 |
22 | generateAuthenticationData(): AuthenticationData {
23 | if (!this._token) {
24 | throw new Error("No token provided for TokenAuth");
25 | }
26 | return {
27 | session: this._session,
28 | type: this._type,
29 | token: this._token,
30 | };
31 | }
32 |
33 | setToken(token: string) {
34 | this._token = token;
35 | }
36 |
37 | get type(): string {
38 | return this._type;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/mocks/event.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export function createEvent(type, id = null, sender = null) {
10 | return {type, event_id: id, sender};
11 | }
12 |
13 | export function withContent(content, event) {
14 | return Object.assign({}, event, {content});
15 | }
16 |
17 | export function withSender(sender, event) {
18 | return Object.assign({}, event, {sender});
19 | }
20 |
21 | export function withTextBody(body, event) {
22 | return withContent({body, msgtype: "m.text"}, event);
23 | }
24 |
25 | export function withTxnId(txnId, event) {
26 | return Object.assign({}, event, {unsigned: {transaction_id: txnId}});
27 | }
28 |
29 | export function withRedacts(redacts, reason, event) {
30 | return Object.assign({redacts, content: {reason}}, event);
31 | }
32 |
33 | export function withReply(replyToId, event) {
34 | return withContent({
35 | "m.relates_to": {
36 | "m.in_reply_to": {
37 | "event_id": replyToId
38 | }
39 | }
40 | }, event);
41 | }
42 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/mic-unmuted.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/room/timeline/ReactionsView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {ListView} from "../../../general/ListView";
10 | import {TemplateView} from "../../../general/TemplateView";
11 |
12 | export class ReactionsView extends ListView {
13 | constructor(reactionsViewModel) {
14 | const options = {
15 | className: "Timeline_messageReactions",
16 | tagName: "div",
17 | list: reactionsViewModel.reactions,
18 | onItemClick: reactionView => reactionView.onClick(),
19 | }
20 | super(options, reactionVM => new ReactionView(reactionVM));
21 | }
22 | }
23 |
24 | class ReactionView extends TemplateView {
25 | render(t, vm) {
26 | return t.button({
27 | className: {
28 | active: vm => vm.isActive,
29 | pending: vm => vm.isPending
30 | },
31 | }, [vm.key, " ", vm => `${vm.count}`]);
32 | }
33 |
34 | onClick() {
35 | this.value.toggle();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/icons/chevron-down.svg:
--------------------------------------------------------------------------------
1 |
2 |
37 |
--------------------------------------------------------------------------------
/src/matrix/storage/idb/stores/InviteStore.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import {Store} from "../Store";
9 | import {MemberData} from "./RoomMemberStore";
10 |
11 | // TODO: Move to Invite when that's TypeScript.
12 | export interface InviteData {
13 | roomId: string;
14 | isEncrypted: boolean;
15 | isDirectMessage: boolean;
16 | name?: string;
17 | avatarUrl?: string;
18 | avatarColorId: number;
19 | canonicalAlias?: string;
20 | timestamp: number;
21 | joinRule: string;
22 | inviter?: MemberData;
23 | }
24 |
25 | export class InviteStore {
26 | private _inviteStore: Store;
27 |
28 | constructor(inviteStore: Store) {
29 | this._inviteStore = inviteStore;
30 | }
31 |
32 | getAll(): Promise {
33 | return this._inviteStore.selectAll();
34 | }
35 |
36 | set(invite: InviteData): void {
37 | this._inviteStore.put(invite);
38 | }
39 |
40 | remove(roomId: string): void {
41 | this._inviteStore.delete(roomId);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/matrix/error.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export class WrappedError extends Error {
10 | constructor(message, cause) {
11 | super(`${message}: ${cause.message}`);
12 | this.cause = cause;
13 | }
14 |
15 | get name() {
16 | return "WrappedError";
17 | }
18 | }
19 |
20 | export class HomeServerError extends Error {
21 | constructor(method, url, body, status) {
22 | super(`${body ? body.error : status} on ${method} ${url}`);
23 | this.errcode = body ? body.errcode : null;
24 | this.retry_after_ms = body ? body.retry_after_ms : 0;
25 | this.statusCode = status;
26 | }
27 |
28 | get name() {
29 | return "HomeServerError";
30 | }
31 | }
32 |
33 | export {AbortError} from "../utils/error";
34 |
35 | export class ConnectionError extends Error {
36 | constructor(message, isTimeout) {
37 | super(message || "ConnectionError");
38 | this.isTimeout = isTimeout;
39 | }
40 |
41 | get name() {
42 | return "ConnectionError";
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/platform/web/legacy-polyfill.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | // polyfills needed for IE11
10 | import Promise from "es6-promise/lib/es6-promise/promise.js";
11 | import {checkNeedsSyncPromise} from "../../matrix/storage/idb/utils";
12 |
13 | if (typeof window.Promise === "undefined") {
14 | window.Promise = Promise;
15 | // TODO: should be awaited before opening any session in the picker
16 | checkNeedsSyncPromise();
17 | }
18 | import "core-js/stable";
19 | import "regenerator-runtime/runtime";
20 | import "mdn-polyfills/Element.prototype.closest";
21 | // olm.init needs utf-16le, and this polyfill was
22 | // the only one I could find supporting it.
23 | // TODO: because the library sees a commonjs environment,
24 | // it will also include the file supporting *all* the encodings,
25 | // weighing a good extra 500kb :-(
26 | import "text-encoding";
27 |
28 | // TODO: contribute this to mdn-polyfills
29 | if (!Element.prototype.remove) {
30 | Element.prototype.remove = function remove() {
31 | this.parentNode.removeChild(this);
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/src/platform/web/theming/shared/color.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import * as pkg from 'off-color';
9 | const offColor = pkg.offColor ?? pkg.default.offColor;
10 |
11 | export function derive(value, operation, argument, isDark) {
12 | const argumentAsNumber = parseInt(argument);
13 | if (isDark) {
14 | // For dark themes, invert the operation
15 | if (operation === 'darker') {
16 | operation = "lighter";
17 | }
18 | else if (operation === 'lighter') {
19 | operation = "darker";
20 | }
21 | }
22 | switch (operation) {
23 | case "darker": {
24 | const newColorString = offColor(value).darken(argumentAsNumber / 100).hex();
25 | return newColorString;
26 | }
27 | case "lighter": {
28 | const newColorString = offColor(value).lighten(argumentAsNumber / 100).hex();
29 | return newColorString;
30 | }
31 | case "alpha": {
32 | return offColor(value).rgba(argumentAsNumber / 100);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/platform/web/ui/css/themes/element/error.css:
--------------------------------------------------------------------------------
1 | .ErrorView_block {
2 | background: var(--error-color);
3 | color: var(--fixed-white);
4 | margin: 16px;
5 | }
6 |
7 | .ErrorView.ErrorView_inline {
8 | color: var(--error-color);
9 | margin: 4px 0;
10 | padding: 4px 0;
11 | }
12 |
13 | .ErrorView.ErrorView_inline > p {
14 | margin: 0;
15 | }
16 |
17 | .ErrorView {
18 | font-weight: bold;
19 | margin: 16px;
20 | border-radius: 8px;
21 | padding: 12px;
22 | display: flex;
23 | gap: 8px;
24 | }
25 |
26 | .ErrorView_message {
27 | flex-basis: 0;
28 | flex-grow: 1;
29 | margin: 0px;
30 | word-break: break-all;
31 | word-break: break-word;
32 | align-self: center;
33 | }
34 |
35 | .ErrorView_submit {
36 | align-self: end;
37 | }
38 |
39 | .ErrorView_close {
40 | align-self: start;
41 | width: 16px;
42 | height: 16px;
43 | border: none;
44 | background: none;
45 | background-repeat: no-repeat;
46 | background-size: contain;
47 | cursor: pointer;
48 | }
49 |
50 | .ErrorView_block .ErrorView_close {
51 | background-image: url('icons/clear.svg?primary=fixed-white');
52 | }
53 |
54 | .ErrorView_inline .ErrorView_close {
55 | background-image: url('icons/clear.svg?primary=text-color');
56 | }
57 |
--------------------------------------------------------------------------------
/src/matrix/verification/SAS/stages/SendKeyStage.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 | import {BaseSASVerificationStage} from "./BaseSASVerificationStage";
9 | import {VerificationEventType} from "../channel/types";
10 | import {CalculateSASStage} from "./CalculateSASStage";
11 |
12 | export class SendKeyStage extends BaseSASVerificationStage {
13 | async completeStage() {
14 | await this.log.wrap("SendKeyStage.completeStage", async (log) => {
15 | const ourSasKey = this.olmSAS.get_pubkey();
16 | await this.channel.send(VerificationEventType.Key, {key: ourSasKey}, log);
17 | /**
18 | * We may have already got the key in SendAcceptVerificationStage,
19 | * in which case waitForEvent will return a resolved promise with
20 | * that content. Otherwise, waitForEvent will actually wait for the
21 | * key message.
22 | */
23 | await this.channel.waitForEvent(VerificationEventType.Key);
24 | this.setNextStage(new CalculateSASStage(this.options));
25 | });
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/matrix/registration/stages/BaseRegistrationStage.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import type {AuthenticationData, RegistrationParams} from "../types";
10 |
11 | export abstract class BaseRegistrationStage {
12 | protected _session: string;
13 | protected _nextStage: BaseRegistrationStage;
14 | protected readonly _params?: Record
15 |
16 | constructor(session: string, params?: RegistrationParams) {
17 | this._session = session;
18 | this._params = params;
19 | }
20 |
21 | /**
22 | * eg: m.login.recaptcha or m.login.dummy
23 | */
24 | abstract get type(): string;
25 |
26 | /**
27 | * This method should return auth part that must be provided to
28 | * /register endpoint to successfully complete this stage
29 | */
30 | /** @internal */
31 | abstract generateAuthenticationData(): AuthenticationData;
32 |
33 | setNextStage(stage: BaseRegistrationStage) {
34 | this._nextStage = stage;
35 | }
36 |
37 | get nextStage(): BaseRegistrationStage {
38 | return this._nextStage;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/domain/session/room/timeline/UpdateAction.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2020 Bruno Windels
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | export class UpdateAction {
10 | constructor(remove, update, replace, updateParams) {
11 | this._remove = remove;
12 | this._update = update;
13 | this._replace = replace;
14 | this._updateParams = updateParams;
15 | }
16 |
17 | get shouldReplace() {
18 | return this._replace;
19 | }
20 |
21 | get shouldRemove() {
22 | return this._remove;
23 | }
24 |
25 | get shouldUpdate() {
26 | return this._update;
27 | }
28 |
29 | get updateParams() {
30 | return this._updateParams;
31 | }
32 |
33 | static Remove() {
34 | return new UpdateAction(true, false, false, null);
35 | }
36 |
37 | static Update(newParams) {
38 | return new UpdateAction(false, true, false, newParams);
39 | }
40 |
41 | static Nothing() {
42 | return new UpdateAction(false, false, false, null);
43 | }
44 |
45 | static Replace(params) {
46 | return new UpdateAction(false, false, true, params);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/domain/ErrorViewModel.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2023 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import { ViewModel, Options as BaseOptions } from "./ViewModel";
10 | import {submitLogsFromSessionToDefaultServer} from "./rageshake";
11 | import type { Session } from "../matrix/Session";
12 | import type {SegmentType} from "./navigation/index";
13 |
14 | type Options = {
15 | error: Error
16 | session: Session,
17 | onClose: () => void
18 | } & BaseOptions;
19 |
20 | export class ErrorViewModel = Options> extends ViewModel {
21 | get message(): string {
22 | return this.error.message;
23 | }
24 |
25 | get error(): Error {
26 | return this.getOption("error");
27 | }
28 |
29 | close() {
30 | this.getOption("onClose")();
31 | }
32 |
33 | async submitLogs(): Promise {
34 | try {
35 | await submitLogsFromSessionToDefaultServer(this.getOption("session"), this.platform);
36 | return true;
37 | } catch (err) {
38 | return false;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/platform/web/ui/session/rightpanel/MemberListView.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2025 New Vector Ltd.
3 | Copyright 2021 The Matrix.org Foundation C.I.C.
4 |
5 | SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
6 | Please see LICENSE files in the repository root for full details.
7 | */
8 |
9 | import {LazyListView} from "../../general/LazyListView";
10 | import {MemberTileView} from "./MemberTileView.js";
11 | import {TemplateView} from "../../general/TemplateView";
12 |
13 | export class MemberListView extends TemplateView {
14 | render(t, vm) {
15 | const list = new LazyListView({
16 | list: vm.memberTileViewModels,
17 | className: "MemberListView__list",
18 | itemHeight: 40
19 | }, tileViewModel => new MemberTileView(tileViewModel));
20 | return t.div({ className: "MemberListView" }, [
21 | t.div({ className: "MemberListView__invite-container" }, [
22 | t.button(
23 | {
24 | className: "MemberListView__invite-btn button-action primary",
25 | onClick: () => vm.openInvitePanel(),
26 | },
27 | vm.i18n`Invite to this room`
28 | ),
29 | ]),
30 | t.view(list),
31 | ]);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/scripts/sdk/transform-paths.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | This script transforms the string literals in the sdk path files to adjust paths
5 | from what they are at development time to what they will be in the sdk package.
6 |
7 | It does this by looking in all string literals in the paths file and looking for file names
8 | that we expect and need replacing (as they are bundled with the sdk).
9 |
10 | Usage: ./transform-paths.js