merge: upstream cinny v4.12.7 into lotus

Brings the 4.12.7 fixes: "%" sign wrapping below the composer input and
emoji autocompletion overwriting the preceding element. The release's
security half (sanitize-html, react-router-dom) was already applied in
91def3ad (we are on newer versions than upstream ships).

Resolved: kept our package.json/lockfile (the fork is ahead on every
dependency — React 19, router 7, i18next 26…), version 4.12.7-lotus, our
README/CONTRIBUTING, our "Lotus Chat vN" branding via pkg.version, and
the deleted GitHub-only workflows stay deleted. Note upstream announces
it is replacing matrix-js-sdk with its own SDK and pausing PRs; future
syncs will get harder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
2026-09-15 13:07:32 -04:00
co-authored by Claude Opus 5
14 changed files with 106 additions and 33 deletions
+1 -1
View File
@@ -57,7 +57,7 @@ body:
required: true
- type: textarea
attributes:
label: Environement
label: Environment
description: |
Please provide information about your environment. Include the following:
- OS:
+9
View File
@@ -0,0 +1,9 @@
### Description
<!-- Please include a summary of the change. -->
Fixes #
### Checklist:
- [ ] I have read and understood the [Contribution policy](https://github.com/ajbura/cinny/blob/dev/CONTRIBUTING.md).
-1
View File
@@ -3,4 +3,3 @@ node_modules
package.json
package-lock.json
LICENSE
README.md
+1 -1
View File
@@ -11,7 +11,7 @@ RUN npm run build
## App
FROM nginx:1.29.8-alpine
FROM nginx:1.31.5-alpine
COPY --from=builder /src/dist /app
COPY --from=builder /src/docker-nginx.conf /etc/nginx/conf.d/default.conf
+60
View File
@@ -0,0 +1,60 @@
# Developing Cinny
> [!TIP]
> We recommend using a version manager as versions change very quickly.
> You will likely need to switch between multiple Node.js versions based
> on the needs of different projects you're working on. [NVM-windows]
> on Windows and [nvm] on Linux/macOS are pretty good choices. Recommended
> nodejs version is Krypton LTS (v24.13.1).
[nvm-windows]: https://github.com/coreybutler/nvm-windows#installation--upgrades
[nvm]: https://github.com/nvm-sh/nvm
Execute the following commands to start a development server:
```sh
npm ci # Installs all dependencies
npm start # Serve a development version
```
To build the app:
```sh
npm run build # Compiles the app into the dist/ directory
```
To commit changes:
```sh
npm run commit
```
## Running with Docker
This repository includes a Dockerfile, which builds the application from
source and serves it with Nginx on port 80. To use this locally, you can
build the container like so:
```
docker build -t cinny:latest .
```
You can then run the container you've built with a command similar to this:
```
docker run -p 8080:80 cinny:latest
```
This will forward your `localhost` port 8080 to the container's port 80.
You can visit the app in your browser by navigating to `http://localhost:8080`.
## Code formatting
We use [ESLint](https://eslint.org/) for clean and stylistically
consistent code syntax, so make sure your pull request follow it.
## Helpful links
- [BEM methodology](http://getbem.com/introduction/)
- [Atomic design](https://bradfrost.com/blog/post/atomic-web-design/)
- [Matrix JavaScript SDK documentation](https://matrix-org.github.io/matrix-js-sdk/index.html)
+6 -6
View File
@@ -129,12 +129,12 @@ Lotus Chat has a desktop app for Windows, macOS, and Linux. It wraps the same we
### Download
Operating System | Download
---|---
Windows | [Get the installer (.exe)](https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/LotusChat-x86_64-setup.exe)
Linux (AppImage, any distro) | [Get the AppImage](https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/LotusChat-x86_64.AppImage)
Linux (Debian/Ubuntu) | [Get the .deb](https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/LotusChat-x86_64.deb)
Linux (Arch/CachyOS/EndeavourOS) | [Get the .pkg.tar.zst](https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/LotusChat-x86_64.pkg.tar.zst) — install with `pacman -U LotusChat-x86_64.pkg.tar.zst`
| Operating System | Download |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Windows | [Get the installer (.exe)](https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/LotusChat-x86_64-setup.exe) |
| Linux (AppImage, any distro) | [Get the AppImage](https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/LotusChat-x86_64.AppImage) |
| Linux (Debian/Ubuntu) | [Get the .deb](https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/LotusChat-x86_64.deb) |
| Linux (Arch/CachyOS/EndeavourOS) | [Get the .pkg.tar.zst](https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/LotusChat-x86_64.pkg.tar.zst) — install with `pacman -U LotusChat-x86_64.pkg.tar.zst` |
All Linux builds need `webkit2gtk-4.1` and, for calls to work, GStreamer's `good`/`bad`/`ugly`/`libav` plugin sets (the pacman package pulls these in automatically; on the AppImage/.deb, install them via your package manager if joining a call shows "browser does not support WebRTC").
+1
View File
@@ -3,6 +3,7 @@
## Insert wasm type into nginx mime.types file so they load correctly.
`/etc/nginx/mime.types`:
```
types {
..
+11 -7
View File
@@ -39,6 +39,7 @@ services:
```
### 1a. MAS `config.yaml` — the parts that matter
After `config generate` (which fills in `secrets.keys` + `encryption`), set:
```yaml
@@ -50,16 +51,17 @@ database:
matrix:
homeserver: localhost # the server_name
endpoint: http://synapse:8008/
secret: "REPLACE_WITH_A_LONG_SHARED_ADMIN_TOKEN"
secret: 'REPLACE_WITH_A_LONG_SHARED_ADMIN_TOKEN'
clients:
- client_id: "0000000000000000000SYNAPSE"
- client_id: '0000000000000000000SYNAPSE'
client_auth_method: client_secret_basic
client_secret: "REPLACE_WITH_A_SHARED_CLIENT_SECRET"
client_secret: 'REPLACE_WITH_A_SHARED_CLIENT_SECRET'
passwords: # so you can create a local test account in the MAS UI
enabled: true
```
### 1b. Synapse `homeserver.yaml` — delegate auth to MAS
See `synapse-msc3861.yaml` in this folder; the key block is:
```yaml
@@ -67,17 +69,18 @@ experimental_features:
msc3861:
enabled: true
issuer: http://localhost:8090/
client_id: "0000000000000000000SYNAPSE"
client_id: '0000000000000000000SYNAPSE'
client_auth_method: client_secret_basic
client_secret: "REPLACE_WITH_A_SHARED_CLIENT_SECRET" # == MAS clients[].client_secret
admin_token: "REPLACE_WITH_A_LONG_SHARED_ADMIN_TOKEN" # == MAS matrix.secret
account_management_url: "http://localhost:8090/account"
client_secret: 'REPLACE_WITH_A_SHARED_CLIENT_SECRET' # == MAS clients[].client_secret
admin_token: 'REPLACE_WITH_A_LONG_SHARED_ADMIN_TOKEN' # == MAS matrix.secret
account_management_url: 'http://localhost:8090/account'
```
Create a test user via the MAS UI (`http://localhost:8090/`) or
`docker compose exec mas mas-cli manage register-user`.
Sanity check discovery (the client relies on this):
```bash
curl -s http://localhost:8008/.well-known/matrix/client | jq '."m.authentication"'
# -> { "issuer": "http://localhost:8090/", "account": "http://localhost:8090/account" }
@@ -108,5 +111,6 @@ See **section N** of `../../LOTUS_TESTING.md` for the actual pass/fail steps
revocation, account-management link, and the non-OIDC-regression check).
## Files here
- `synapse-msc3861.yaml` — the Synapse experimental-features delta.
- `config.local.json` — the Lotus `public/config.json` override.
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "lotus-chat",
"version": "4.12.3-lotus",
"version": "4.12.7-lotus",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "lotus-chat",
"version": "4.12.3-lotus",
"version": "4.12.7-lotus",
"hasInstallScript": true,
"license": "AGPL-3.0-only",
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "lotus-chat",
"version": "4.12.3-lotus",
"version": "4.12.7-lotus",
"description": "Lotus Chat — Matrix client for Lotus Guild",
"main": "index.js",
"type": "module",
+4 -4
View File
@@ -268,15 +268,15 @@ export const getPointUntilChar = (
return targetPoint;
};
export const getPrevWorldRange = (editor: Editor): BaseRange | undefined => {
export const getPrevWordRange = (editor: Editor): BaseRange | undefined => {
const { selection } = editor;
if (!selection || !Range.isCollapsed(selection)) return undefined;
const [cursorPoint] = Range.edges(selection);
const worldStartPoint = getPointUntilChar(editor, cursorPoint, {
const wordStartPoint = getPointUntilChar(editor, cursorPoint, {
reverse: true,
match: (char) => char === ' ',
match: (char) => char === ' ' || char === '',
});
return worldStartPoint && Editor.range(editor, worldStartPoint, cursorPoint);
return wordStartPoint && Editor.range(editor, wordStartPoint, cursorPoint);
};
export const isEmptyEditor = (editor: Editor): boolean => {
+2 -2
View File
@@ -44,7 +44,7 @@ import {
AutocompletePrefix,
AutocompleteQuery,
getAutocompleteQuery,
getPrevWorldRange,
getPrevWordRange,
resetEditor,
RoomMentionAutocomplete,
UserMentionAutocomplete,
@@ -806,7 +806,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
sendTypingStatus(!isEmptyEditor(editor));
}
const prevWordRange = getPrevWorldRange(editor);
const prevWordRange = getPrevWordRange(editor);
const query = prevWordRange
? getAutocompleteQuery<AutocompletePrefix>(editor, prevWordRange, AUTOCOMPLETE_PREFIXES)
: undefined;
@@ -35,7 +35,7 @@ import {
createEmoticonElement,
customHtmlEqualsPlainText,
getAutocompleteQuery,
getPrevWorldRange,
getPrevWordRange,
htmlToEditorInput,
moveCursor,
plainToEditorInput,
@@ -281,7 +281,7 @@ export const MessageEditor = as<'div', MessageEditorProps>(
return;
}
const prevWordRange = getPrevWorldRange(editor);
const prevWordRange = getPrevWordRange(editor);
const query = prevWordRange
? getAutocompleteQuery<AutocompletePrefix>(editor, prevWordRange, AUTOCOMPLETE_PREFIXES)
: undefined;
+3 -3
View File
@@ -4,9 +4,9 @@ export const useAccountManagementActions = () => {
const actions = useMemo(
() => ({
profile: 'org.matrix.profile',
sessionsList: 'org.matrix.sessions_list',
sessionView: 'org.matrix.session_view',
sessionEnd: 'org.matrix.session_end',
sessionsList: 'org.matrix.devices_list',
sessionView: 'org.matrix.device_view',
sessionEnd: 'org.matrix.device_delete',
accountDeactivate: 'org.matrix.account_deactivate',
crossSigningReset: 'org.matrix.cross_signing_reset',
}),