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 required: true
- type: textarea - type: textarea
attributes: attributes:
label: Environement label: Environment
description: | description: |
Please provide information about your environment. Include the following: Please provide information about your environment. Include the following:
- OS: - 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 -2
View File
@@ -2,5 +2,4 @@ dist
node_modules node_modules
package.json package.json
package-lock.json package-lock.json
LICENSE LICENSE
README.md
+1 -1
View File
@@ -11,7 +11,7 @@ RUN npm run build
## App ## App
FROM nginx:1.29.8-alpine FROM nginx:1.31.5-alpine
COPY --from=builder /src/dist /app COPY --from=builder /src/dist /app
COPY --from=builder /src/docker-nginx.conf /etc/nginx/conf.d/default.conf 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 ### Download
Operating System | Download | Operating System | Download |
---|--- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
Windows | [Get the installer (.exe)](https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/LotusChat-x86_64-setup.exe) | 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 (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 (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` | 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"). 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. ## Insert wasm type into nginx mime.types file so they load correctly.
`/etc/nginx/mime.types`: `/etc/nginx/mime.types`:
``` ```
types { types {
.. ..
+13 -9
View File
@@ -39,6 +39,7 @@ services:
``` ```
### 1a. MAS `config.yaml` — the parts that matter ### 1a. MAS `config.yaml` — the parts that matter
After `config generate` (which fills in `secrets.keys` + `encryption`), set: After `config generate` (which fills in `secrets.keys` + `encryption`), set:
```yaml ```yaml
@@ -48,18 +49,19 @@ http:
database: database:
uri: postgresql://synapse:pw@postgres/synapse uri: postgresql://synapse:pw@postgres/synapse
matrix: matrix:
homeserver: localhost # the server_name homeserver: localhost # the server_name
endpoint: http://synapse:8008/ endpoint: http://synapse:8008/
secret: "REPLACE_WITH_A_LONG_SHARED_ADMIN_TOKEN" secret: 'REPLACE_WITH_A_LONG_SHARED_ADMIN_TOKEN'
clients: clients:
- client_id: "0000000000000000000SYNAPSE" - client_id: '0000000000000000000SYNAPSE'
client_auth_method: client_secret_basic 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 passwords: # so you can create a local test account in the MAS UI
enabled: true enabled: true
``` ```
### 1b. Synapse `homeserver.yaml` — delegate auth to MAS ### 1b. Synapse `homeserver.yaml` — delegate auth to MAS
See `synapse-msc3861.yaml` in this folder; the key block is: See `synapse-msc3861.yaml` in this folder; the key block is:
```yaml ```yaml
@@ -67,17 +69,18 @@ experimental_features:
msc3861: msc3861:
enabled: true enabled: true
issuer: http://localhost:8090/ issuer: http://localhost:8090/
client_id: "0000000000000000000SYNAPSE" client_id: '0000000000000000000SYNAPSE'
client_auth_method: client_secret_basic client_auth_method: client_secret_basic
client_secret: "REPLACE_WITH_A_SHARED_CLIENT_SECRET" # == MAS clients[].client_secret client_secret: 'REPLACE_WITH_A_SHARED_CLIENT_SECRET' # == MAS clients[].client_secret
admin_token: "REPLACE_WITH_A_LONG_SHARED_ADMIN_TOKEN" # == MAS matrix.secret admin_token: 'REPLACE_WITH_A_LONG_SHARED_ADMIN_TOKEN' # == MAS matrix.secret
account_management_url: "http://localhost:8090/account" account_management_url: 'http://localhost:8090/account'
``` ```
Create a test user via the MAS UI (`http://localhost:8090/`) or Create a test user via the MAS UI (`http://localhost:8090/`) or
`docker compose exec mas mas-cli manage register-user`. `docker compose exec mas mas-cli manage register-user`.
Sanity check discovery (the client relies on this): Sanity check discovery (the client relies on this):
```bash ```bash
curl -s http://localhost:8008/.well-known/matrix/client | jq '."m.authentication"' curl -s http://localhost:8008/.well-known/matrix/client | jq '."m.authentication"'
# -> { "issuer": "http://localhost:8090/", "account": "http://localhost:8090/account" } # -> { "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). revocation, account-management link, and the non-OIDC-regression check).
## Files here ## Files here
- `synapse-msc3861.yaml` — the Synapse experimental-features delta. - `synapse-msc3861.yaml` — the Synapse experimental-features delta.
- `config.local.json` — the Lotus `public/config.json` override. - `config.local.json` — the Lotus `public/config.json` override.
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "lotus-chat", "name": "lotus-chat",
"version": "4.12.3-lotus", "version": "4.12.7-lotus",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "lotus-chat", "name": "lotus-chat",
"version": "4.12.3-lotus", "version": "4.12.7-lotus",
"hasInstallScript": true, "hasInstallScript": true,
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "lotus-chat", "name": "lotus-chat",
"version": "4.12.3-lotus", "version": "4.12.7-lotus",
"description": "Lotus Chat — Matrix client for Lotus Guild", "description": "Lotus Chat — Matrix client for Lotus Guild",
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
+4 -4
View File
@@ -268,15 +268,15 @@ export const getPointUntilChar = (
return targetPoint; return targetPoint;
}; };
export const getPrevWorldRange = (editor: Editor): BaseRange | undefined => { export const getPrevWordRange = (editor: Editor): BaseRange | undefined => {
const { selection } = editor; const { selection } = editor;
if (!selection || !Range.isCollapsed(selection)) return undefined; if (!selection || !Range.isCollapsed(selection)) return undefined;
const [cursorPoint] = Range.edges(selection); const [cursorPoint] = Range.edges(selection);
const worldStartPoint = getPointUntilChar(editor, cursorPoint, { const wordStartPoint = getPointUntilChar(editor, cursorPoint, {
reverse: true, 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 => { export const isEmptyEditor = (editor: Editor): boolean => {
+2 -2
View File
@@ -44,7 +44,7 @@ import {
AutocompletePrefix, AutocompletePrefix,
AutocompleteQuery, AutocompleteQuery,
getAutocompleteQuery, getAutocompleteQuery,
getPrevWorldRange, getPrevWordRange,
resetEditor, resetEditor,
RoomMentionAutocomplete, RoomMentionAutocomplete,
UserMentionAutocomplete, UserMentionAutocomplete,
@@ -806,7 +806,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
sendTypingStatus(!isEmptyEditor(editor)); sendTypingStatus(!isEmptyEditor(editor));
} }
const prevWordRange = getPrevWorldRange(editor); const prevWordRange = getPrevWordRange(editor);
const query = prevWordRange const query = prevWordRange
? getAutocompleteQuery<AutocompletePrefix>(editor, prevWordRange, AUTOCOMPLETE_PREFIXES) ? getAutocompleteQuery<AutocompletePrefix>(editor, prevWordRange, AUTOCOMPLETE_PREFIXES)
: undefined; : undefined;
@@ -35,7 +35,7 @@ import {
createEmoticonElement, createEmoticonElement,
customHtmlEqualsPlainText, customHtmlEqualsPlainText,
getAutocompleteQuery, getAutocompleteQuery,
getPrevWorldRange, getPrevWordRange,
htmlToEditorInput, htmlToEditorInput,
moveCursor, moveCursor,
plainToEditorInput, plainToEditorInput,
@@ -281,7 +281,7 @@ export const MessageEditor = as<'div', MessageEditorProps>(
return; return;
} }
const prevWordRange = getPrevWorldRange(editor); const prevWordRange = getPrevWordRange(editor);
const query = prevWordRange const query = prevWordRange
? getAutocompleteQuery<AutocompletePrefix>(editor, prevWordRange, AUTOCOMPLETE_PREFIXES) ? getAutocompleteQuery<AutocompletePrefix>(editor, prevWordRange, AUTOCOMPLETE_PREFIXES)
: undefined; : undefined;
+3 -3
View File
@@ -4,9 +4,9 @@ export const useAccountManagementActions = () => {
const actions = useMemo( const actions = useMemo(
() => ({ () => ({
profile: 'org.matrix.profile', profile: 'org.matrix.profile',
sessionsList: 'org.matrix.sessions_list', sessionsList: 'org.matrix.devices_list',
sessionView: 'org.matrix.session_view', sessionView: 'org.matrix.device_view',
sessionEnd: 'org.matrix.session_end', sessionEnd: 'org.matrix.device_delete',
accountDeactivate: 'org.matrix.account_deactivate', accountDeactivate: 'org.matrix.account_deactivate',
crossSigningReset: 'org.matrix.cross_signing_reset', crossSigningReset: 'org.matrix.cross_signing_reset',
}), }),