* Support RTL text in the room input field
set the correct direction for text according to the language written in
* Make all input RTLable
Co-authored-by: Krishan <33421343+kfiven@users.noreply.github.com>
* Generate blurhash client side
* Make blurhash generation faster
* Simple blurhash display support
* Make image display simpler
* Support non square images
* Don't attach video blurhash to thumbnail
* Add video display support
* Ignore alt tag missing warning
Co-authored-by: Ajay Bura <32841439+ajbura@users.noreply.github.com>
* Remove comments
* Show custom emoji first in suggestions
* Show global image packs in emoji picker
* Display emoji and sticker in room settings
* Fix some pack not visible in emojiboard
* WIP
* Add/delete/rename images to exisitng packs
* Change pack avatar, name & attribution
* Add checkbox to make pack global
* Bug fix
* Create or delete pack
* Add personal emoji in settings
* Show global pack selector in settings
* Show space emoji in emojiboard
* Send custom emoji reaction as mxc
* Render stickers as stickers
* Fix sticker jump bug
* Fix reaction width
* Fix stretched custom emoji
* Fix sending space emoji in message
* Remove unnessesary comments
* Send user pills
* Fix pill generating regex
* Add support for sending stickers
This allows users to mark all rooms in a space as read, matching similar
features found in other popular chat applications.
We opted to place the mark as read button at the top of the list instead
of next to the add user button like in room options since we felt this
will be the most-used button in the list.
Fixes#645.
Co-authored-by: Maple <mapletree.dv@gmail.com>
Co-authored-by: Maple <mapletree.dv@gmail.com>
From https://spec.matrix.org/v1.3/appendices/#matrixto-navigation:
The components of the matrix.to URI (<identifier> and <extra parameter>) are to be percent-encoded as per RFC 3986.
Historically, clients have not produced URIs which are fully encoded. Clients should try to interpret these cases to the best of their ability. For example, an unencoded room alias should still work within the client if possible
* nodejs 17.9.0 also works
* Add github sponser link
* Add Public PGP key of signed tarball
* Update README.md
* Add download badge also.
* Add docker pulls
* Reduce dependence on third-party build scripts in release pipeline
This removes one third-party build script from the release
pipeline for the release tar.gz, though one is still used in the
now-separate netlify deploy.
* Reduce GITHUB_TOKEN perms in actions when using 3rd party scripts
This avoids allowing third parties to arbitrarily overwrite the
repository.
* Replace PGP signing action with the bash script from the same
The PGP signing action ultimately just calls gpg with arguments
set in
https://github.com/actionhippie/gpgsign/blob/v1/overlay/usr/local/bin/entrypoint
so its rather trivial to simply take the required arguments and
put them directly in CI.
This is substantially safer than the PGP signing action used as the
action currently downloads, unverified and un-pinned, a docker
image in order to access PGP.
* pasting should focus the message field
also refactored a small amount to use KeyEvent.code
instead of KeyEvent.keyCode, which is deprecated.
fixesajbura/cinny#544
* fix lint
* comments
* Initial display support
* Use better colors for error in math parsing
* Parse math markdown
* Use proper jsx
* Better copy support
* use css var directly
* Remove console.debug call
* Lazy load math module
* Show fallback while katex is loading
* Now adapting to small screen sizes, needs improvements
* Fix that site only gets into mobile mode when resized
* - Added navigation event triggered if user requests to return to navigation on compact screens
- People drawer wont be shown on compact screens
- Still accessible using settings
- would be duplicated UI
- mobileSize is now compactSize
* Put threshold for collapsing the base UI in a shared file
* Switch to a more simple solution using CSS media queries over JS
- Move back button to the left a bit so it doesnt get in touch with room icon
* switch from component-individual-thresholds to device-type thresholds
- <750px: Mobile
- <900px: Tablet
- >900px: Desktop
* Make Settings drawer component collapse on mobile
* Fix EmojiBoard not showing up and messing up UI when screen is smaller than 360px
* Improve code quality; allow passing classNames to IconButton
- remove unnessesary div wrappers
- use dir.side where appropriate
- rename threshold and its mixins to more descriptive names
- Rename "OPEN_NAVIGATION" to "NAVIGATION_OPENED"
* - follow BEM methology
- remove ROOM_SELECTED listener
- rename NAVIGATION_OPENED to OPEN_NAVIGATION where appropriate
- this does NOT changes that ref should be used for changing visability
* Use ref to change visability to avoid re-rendering
* Use ref to change visability to avoid re-rendering
* Fix that room component is not hidden by default.
This resulted in a broken view when application is viewed in mobile size without having selected a room since loading.
* fix: leaving a room should bring one back to navigation
Co-authored-by: Ajay Bura <32841439+ajbura@users.noreply.github.com>
* Fix power level in permissions
Fix allowed value of power level in room permissions, earlier the max value was 100 even if room members have power level more than 100.
* Update RoomPermissions.jsx
* Fixes#434
* Fixes#433
* Prtially fixes#432
* Disable auto labelling of issues
* Use yaml instead of yml as recommended by yaml.org
* shortened the strings
* simplified option description
* Allow node type prop in setting tile
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Update popup window max height
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Add device management setting
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Add password based login
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* truncate long list of verified devices
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Add invite sound
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Add credits
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Change invite sound to google material
* Sort search result by activity
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Optimize generateResults function codes
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Improve jump to unread button
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Remove unused cod
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Fix mark as read not hidding jump to unread btn
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Add notification mark as read action
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Add esc as hotkey to mark room as read
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Add message icons
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Change jump to unread icon
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Add recent section to emoji board
* Add section to emoji board sidebar
* Add emoji limit like element web has
* Ignore custom emojis
* Filter out invalid emojis
* Update heart icon with clock
Co-authored-by: Krishan <33421343+kfiven@users.noreply.github.com>
* Add sort util
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Use sort util for members
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Sort dms by activity
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Sort dms activily
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Chanege roomIdByLastActive func name
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Fix new message no appearing
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Fix room not marking as read
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Fix room automatically gets mark as read
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Fix sending wrong read recipt
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Fix sending message not mark as read
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Move getNotifType function
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Fix bug in getNotiType
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Add isMuted prop in room selector
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Fix muted room show unread indicator
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Fix muted room notification visible in space
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Fix space shows muted room notification on load
Signed-off-by: Ajay Bura <ajbura@gmail.com>
* Toggle room mute when changed from other client
Signed-off-by: Ajay Bura <ajbura@gmail.com>
Signed-off-by: Clament John <cj@hackerlab.in>
fixes#376
When we click view source for an edited message we were showing
the original event (the unedited event) instead of the latest
edited event.
* Simplify production build actions
This merges both the netlify-prod and docker action and also automatically add tarball to releases.
* Delete docker.yaml
* Delete netlify-prod.yaml
* Cosmetic changes and add dockerhub check
* Cosmetic changes
* Fix check runs on Tuesdays only
* Add basic drop overlay
* Prevent crash when dragging text
* Only show popup when files are being dragged
* Make drop box bigger
* Make drag drop overlay without a modal
* Don't show drag drop menu on top of modals
* Use different way to check for modal
* Focus when opening the emoji board and editing a message
* Clean emoji board after closing
* Focus room search and member search
* Resolve conversations
* Use npm ci over install to achive faster and more expectable build results;
Split copy package(-lock).json files and ci then to avoid reinstalling dependencies when not needed => Faster build times
* Stopp adding wasm type to mime.types, its already there (duplicate):
- avoids warning in console
- cleans up
- might have been missing in past nginx:alpine versions but now exists
* Change node tag from alpine and nginx to more specific ones for #260
* Add notifications
* Abide push actions
* Handle browsers not having notification support
* Ask for notification permission after loading
* Make usePermission work without live permission support
* Focus message when clicking the notification
* make const all caps
* Fix usePermission error in Safari
* Fix live permissions
* Remove userActivity and use document.visibilityState instead
* Change setting label to "desktop notifications"
* Check for notification permissions in the settings.js
* Add Auto theme that uses browser's preferred color scheme
This will use dark mode automatically if the browser requests it.
* fixup! Add Auto theme that uses browser's preferred color scheme
* Use a toggle to use system theme
* Display custom emoji in picker
Adds a single category at the start of the emoji picker to display the user's custom emoji
* Show any amount of custom emoji packs in the Emoji Board
* Use thumbnails in emoji picker + mark as emoji
* Fix emoji picker stretching when too many packs are available
* Sprinkle in a few comments for good measure
* Remove emoji-less packs from the emoji picker
* Add support for sending room-local emoji
Does not add support for sending a room's emoji outside of that room, but enables users to
send an emoji if the packs in a room support it. Does not include room emoji in the
picker YET.
* Amend PR #209: Don't freak out if the `pack` tag is missing
* Amending PR: Refactor emojifier, use better method for retrieving packs
* Amending PR: Improve resiliance to bad data in emoji state events
* Amend PR: Remove redundant code, fix crash on edit
* Add support for sending user emoji using autocomplete
What's included:
- An implementation for detecting user emojis
- Addition of user emojis to the emoji autocomplete in the command bar
- Translation of shortcodes into image tags on message sending
What's not included:
- Loading emojis from the active room, loading the user's global emoji packs, loading emoji from spaces
- Selecting custom emoji using the emoji picker
This is a predominantly proof-of-concept change, and everything here may be subject to
architectural review and reworking.
* Amending PR: Allow sending multiple of the same emoji
* Amending PR: Add support for emojis in edited messages
* Amend PR: Apply requested revisions
This commit consists of several small changes, including:
- Fix crash when the user doesn't have the im.ponies.user_emotes account data entry
- Add mx-data-emoticon attribute to command bar emoji
- Rewrite alt text in the command bar interface
- Remove "vertical-align" attribute from sent emoji
* Amending PR: Fix bugs (listed below)
- Fix bug where sending emoji w/ markdown off resulted in a crash
- Fix bug where alt text in the command bar was wrong
* Amending PR: Add support for replacement of twemoji shortcodes
* Amending PR: Fix & refactor getAllEmoji -> getShortcodeToEmoji
* Amending PR: Fix bug: Sending two of the same emoji corrupts message
* Amending PR: Stylistic fixes
* Display messages containing only <7 emoji bigger
* Amending PR: Address mentioned concerns
This fixes several concerns raised during the PR review process. A summary of the changes
implemented is below:
- Size jumbo emoji using the text-h1 class, instead of hardcoding a size
- Increase the emoji limit to 10
- Re-wrap m.text messages in a p tag, fixing a bug where newlines were lost
Line 335 already gives blockquotes their padding. The mixin explicitly sets the right padding back to 0 and the left padding to exactly what it was already set to.
* Fix commands activating anywhere in the input
Writing `The command to leave a channel is /leave` might have had "fun"
consequences for users.
Fixes#155
* Fix go-to commands activating anywhere in the input
While less obtrusive than `/` commands activating anywhere, it seems
logical to only activate completion of those when at the beginning of
the input.
`break-all` meant that links would split mid-word e.g. I observed `email` become `e\nmail`. `break-word` avoids this but also ensures long links still break before overflowing the line length.
* Address 301 redirect issue and Safari regex issue.
* Restored login redirect
as this doesn't not fix the sso redirect #143.
Co-authored-by: Ajay Bura <32841439+ajbura@users.noreply.github.com>
> Please read through [the Discussion rules](https://github.com/cinnyapp/cinny/discussions/2653) and check for both existing [Discussions](https://github.com/cinnyapp/cinny/discussions?discussions_q=) and [Issues](https://github.com/cinnyapp/cinny/issues?q=sort%3Areactions-desc) prior to opening a new Discussion.
- type:markdown
attributes:
value:'# Issue Details'
- type:textarea
attributes:
label:Issue Description
description:|
Provide a detailed description of the issue. Include relevant information, such as:
- The feature or configuration option you encounter the issue with.
- Screenshots, screen recordings, or other supporting media (as needed).
- If this is a regression of an existing issue that was closed or resolved, please include the previous item reference (Discussion, Issue, PR, commit) in your description.
placeholder:|
When I try to send a message in a room, the message doesn't appear in the timeline.
OR
The application crashes when I click on the settings button.
validations:
required:true
- type:textarea
attributes:
label:Expected Behavior
description:|
Describe how you expect Cinny to behave in this situation.
placeholder:|
I expected the message to appear in the room timeline immediately after sending.
OR
The settings panel should open smoothly without any crashes.
validations:
required:true
- type:textarea
attributes:
label:Actual Behavior
description:|
Describe how Cinny actually behaves in this situation. If it is not immediately obvious how the actual behavior differs from the expected behavior described above, please be sure to mention the deviation specifically.
placeholder:|
The application freezes for 3 seconds and then shows a white screen.
validations:
required:true
- type:textarea
attributes:
label:Reproduction Steps
description:|
Provide a detailed set of step-by-step instructions for reproducing this issue.
placeholder:|
1. Open Cinny and log in to my account
2. Navigate to the #general room
3. Type a message in the message box
4. Press Enter to send
5. Notice that the message doesn't appear in the timeline
validations:
required:true
- type:textarea
attributes:
label:Environement
description:|
Please provide information about your environment. Include the following:
- OS:
- Browser:
- Cinny Web Version: (app.cinny.in or self hosted)
- Cinny desktop Version: (appimage or deb or flatpak)
- Matrix Homeserver:
placeholder:|
- OS: Windows 11
- Browser: Chrome 120.0.6099.109
- Cinny Web Version: 3.2.0 (app.cinny.in or self hosted)
- Cinny desktop Version: 3.2.0 (appimage or deb or flatpak)
- Matrix Homeserver: matrix.org (Synapse 1.97.0)
render:text
validations:
required:true
- type:textarea
id:logs
attributes:
label:Relevant Logs
description:|
If applicable, add browser console logs to help explain your problem.
**To get browser console logs:**
- Chrome/Edge: Press F12 → Console tab
- Firefox: Press F12 → Console tab
- Safari: Develop → Show Web Inspector → Console
Please wrap large log outputs in code blocks with triple backticks (```).
placeholder:|
```
Error: Failed to send message
at MessageComposer.sendMessage (composer.js:245)
at HTMLButtonElement.onClick (composer.js:189)
TypeError: Cannot read property 'content' of undefined
at RoomTimeline.render (timeline.js:567)
```
render:shell
validations:
required:false
- type:textarea
attributes:
label:Additional context
description:|
Add any other context about the problem here (e.g., when did this start happening, does it happen on different homeservers, etc.)
placeholder:|
- This started happening after I updated to version 3.2.0
- It only happens in encrypted rooms, not in public rooms
- I've tried on both Firefox and Chrome with the same result
- It works fine on my phone using the same account
- This happens on all homeservers I've tested (matrix.org, mozilla.org)
validations:
required:false
- type:markdown
attributes:
value:|
# User Acknowledgements
> [!TIP]
> Use these links to review the existing Cinny [Discussions](https://github.com/cinnyapp/cinny/discussions?discussions_q=) and [Issues](https://github.com/cinnyapp/cinny/issues?q=sort%3Areactions-desc).
- type:checkboxes#add faqs in future
attributes:
label:'I acknowledge that:'
options:
- label:I have searched the Cinny repository (both open and closed Discussions and Issues) and confirm this is not a duplicate of an existing issue or discussion.
required:true
- label:I have checked the "Preview" tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (` ``` `) on separate lines.
<!-- Please read https://github.com/ajbura/cinny/blob/dev/CONTRIBUTING.md before submitting your pull request -->
### Description
<!-- Please include a summary of the change. Please also include relevant motivation and context. List any dependencies that are required for this change. -->
Fixes #
#### Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
### Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
if:(github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
# the below token should have repo scope and must be manually added by you in the repository's secret
PERSONAL_ACCESS_TOKEN:${{ secrets.CLA_PAT }}
with:
path-to-signatures:'signatures.json'
path-to-document:'https://github.com/cinnyapp/cla/blob/main/cla.md'# e.g. a CLA or a DCO document
# branch should not be protected
branch:'main'
allowlist:ajbura,bot*
#below are the optional inputs - If the optional inputs are not given, then default values will be taken
remote-organization-name:cinnyapp
remote-repository-name:cla
#create-file-commit-message: 'For example: Creating file for storing CLA Signatures'
#signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo'
#custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign'
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
#lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true)
#use-dco-flag: true - If you are using DCO instead of CLA
@@ -5,7 +5,6 @@ First off, thanks for taking the time to contribute! ❤️
All types of contributions are encouraged and valued. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉
> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about:
>
> - Star the project
> - Tweet about it (tag @cinnyapp)
> - Refer this project in your project's readme
@@ -19,8 +18,7 @@ Bug reports and feature suggestions must use descriptive and concise titles and
## Pull requests
> ### Legal Notice
>
> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. You will also be asked to [sign the CLA](https://github.com/cinnyapp/cla) upon submiting your pull request.
> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license.
**NOTE: If you want to add new features, please discuss with maintainers before coding or opening a pull request.** This is to ensure that we are on same track and following our roadmap.
@@ -28,9 +26,9 @@ Bug reports and feature suggestions must use descriptive and concise titles and
<img alt="Follow on Mastodon" src="https://img.shields.io/mastodon/follow/106845779685925461?domain=https%3A%2F%2Ffosstodon.org&logo=mastodon&style=social"></a>
- All patterns use CSS custom properties — adapt to both TDS dark and light themes
- Settings toggle for showing per-message sender profiles
### Voice / Video Call Improvements
- **Element Call 0.19.4**: Upgraded from 0.16.3. Dist copied to `public/element-call/` by vite at build time.
- **Camera default OFF**: Camera no longer persists across sessions via localStorage. Always starts disabled. Optional `cameraOnJoin` setting for explicit opt-in.
- **Deafen button**: Tooltip corrected to "Deafen" / "Undeafen" (was "Turn Off Sound" / "Turn On Sound")
- **Screenshare confirmation**: A confirm dialog appears before screenshare is broadcast to call participants
- **Auto-revert spotlight on screenshare**: When someone starts screensharing, EC normally forces all participants into spotlight view. Patched in `CallControl.ts``onControlMutation()` — detects the screenshare button going `primary` and clicks `gridButton` after 600ms to revert to grid layout. Participants choose to watch screenshare manually.
- **Push to Talk (PTT)**:
- Configurable keybind (default: Space) via Settings > General > Calls
- Mic activates on keydown, deactivates on keyup; mic muted on tab blur/focus to prevent stuck-on mic
- Visual indicator: plain folds `Chip` by default; when LotusGuild TDS is active: orange `PTT — Hold SPACE` / green `● LIVE` in JetBrains Mono
- Listens on both main window and EC iframe `contentWindow` for reliable key capture
- Implemented via `CallControl.setMicrophone()` public method on the widget bridge
- **Mic state preservation**: when enabling PTT mode mid-call, the user's previous mic state is saved and restored when PTT is disabled — prevents unwanted unmute if the user had manually muted before switching to PTT.
- **Noise suppression toggle**: Settings > General > Calls — passes `noiseSuppression` URL parameter to the embedded Element Call widget
- **Call button scoping**: The upstream Cinny 4.12.1 call button (voice + video dropdown) is restricted to DMs and private group chats only. Specifically: direct messages, or invite-only rooms that have no `m.space.parent` state event (i.e. not a space/guild text channel). Public rooms and space channels are excluded to prevent accidental mass-notifications. `Room.tsx` switches to CallView layout when a call embed is active in the current room.
- **Poll display**: `m.poll.start` events (both stable Matrix 1.7 `m.poll` content key and MSC3381 unstable `org.matrix.msc3381.poll.start`) render as read-only poll cards inside the standard message bubble — question and answer options shown. Registered as top-level event renderers AND inside the `EncryptedContent` callback so encrypted polls also display after decryption. "Open in Element to vote" note displayed. Implemented in `PollContent.tsx`.
- **Deleted message placeholder**: Redacted `m.room.message`, `m.room.encrypted`, and `m.sticker` events no longer disappear from the timeline. Instead they reach the existing `RedactedContent` component (trash icon + italic "This message has been deleted" with reason if provided), matching Element, FluffyChat, Commet, and Nheko behaviour. One-line change in the `eventRenderer` filter in `RoomTimeline.tsx`.
- **Picture-in-picture (PiP)**: When navigating away from a call room while in an active call, the call embed shrinks to a 280x158px floating window in the bottom-right corner. The PiP window is **draggable** — drag it anywhere on screen to move it out of the way. Clicking (without dragging) navigates back to the call room. Drag vs click distinguished by a 5px movement threshold; touch drag supported. Imperative style overrides on `callEmbedRef.current` via `useEffect` — a wrapper div cannot be used because `useCallEmbedPlacementSync` writes `top/left/width/height` directly onto that element.
- **Call embed positioning**: `useCallEmbedPlacementSync` uses `getBoundingClientRect()` (not `offsetTop/Left`) for accurate viewport-relative coordinates on the `position:fixed` container. Position is synced immediately on mount via `useEffect` in addition to the ResizeObserver, so the embed is placed correctly the instant the call view renders. The `[pipMode, callVisible]` effect in `CallEmbedProvider` only clears pip-specific styles when actually exiting pip mode — no longer clobbers the position set by `syncCallEmbedPlacement` on every `callVisible` toggle.
- **Dark mode in element-call**: After joining, `CallEmbed.applyStyles()` injects `:root { color-scheme: dark|light }` into the iframe document so `@media (prefers-color-scheme)` rules inside element-call resolve to the correct Cinny theme regardless of the OS system preference. `themeKind` is stored on the `CallEmbed` instance and updated on every `setTheme()` call, so live theme switching also re-injects the CSS. Without this, users with OS light mode would see a white background even when Cinny is in dark mode.
- **Call embed wallpaper**: The user's `chatBackground` pattern (Blueprint, Carbon, Stars…) is applied as the `backgroundImage`/`backgroundColor` of `div[data-call-embed-container]` when the call is in full view (not PiP). The iframe `html, body` is forced to `background: none !important` so the pattern shows through. When `chatBackground` is `none`, behaviour is unchanged.
### Moderation
- **Report Room**: A "Report Room" option in the room header menu (⋮) allows users to report a room to homeserver admins with a reason and abuse category (Spam / Harassment / Inappropriate Content / Other). Calls `POST /_matrix/client/v3/rooms/{roomId}/report` (MSC4151, confirmed supported on matrix.lotusguild.org). Implemented in `ReportRoomModal.tsx` with loading/success/error states.
- **Policy List / Ban List Viewer (MSC2313)**: A "Policy Lists" tab in Room / Space Settings (admin-only, power-level gated) shows all subscribed `m.policy.rule.*` rooms and their contents — banned users, banned rooms, and banned servers — each with entity, reason, and recommendation fields. Subscribe (join the policy room) and Unsubscribe (leave) actions are provided. Enforcement remains solely with the Draupnir bot; this UI is a read-only complement.
### Messaging Enhancements
- **Rich room topics**: Room topics that contain formatted text (bold, links, italic) are now rendered with full HTML formatting. Falls back to plain text if no `formatted_body` is present. Activates when any room admin sets a formatted topic.
- **Edit history viewer**: Clicking the "edited" label on any edited message opens a modal showing every prior version with timestamps. Fetches all `m.replace` relations for the event and displays them oldest-to-newest. Previously the "edited" label was visible but unclickable. E2EE fix: the "Original" entry now uses `getClearContent()` (bypasses the replacing-event chain, returns the decrypted pre-edit body) instead of `event.content` which is still raw ciphertext for encrypted messages — fixes "(no text)" shown for almost all E2EE message originals.
- **Inline GIF preview**: Giphy and Tenor share links sent as plain text auto-embed as animated GIFs inline in the timeline. URL patterns are detected client-side; the image is fetched via the homeserver's `/_matrix/media/v3/preview_url` proxy (no direct contact with Giphy/Tenor from the client). Rendered as `<img loading="lazy">` — respects the existing URL preview enabled/disabled setting.
- **GIF picker**: Giphy-powered GIF search and send. Button appears in the message composer only when `gifApiKey` is set in `config.json`. Sends GIF as `m.image` — fetches blob, uploads via `mx.uploadContent`, sends with `mx.sendMessage`. `FocusTrap` handles click-outside / Escape to close. When TDS is active: dark navy background (`#060c14`), orange dim border, `// GIF_SEARCH` header, CSS overrides for Giphy SDK search bar (dark bg, orange border/focus ring, JetBrains Mono), custom orange scrollbar. All TDS styles live in `lotus-terminal.css.ts` — no runtime `<style>` injection, eliminating flash of unstyled content.
- **Message forwarding**: Forward any message to any room from the message context menu.
- **Draft persistence**: Unsent message drafts survive page reload via `localStorage` (`draft-msg-<roomId>`). Jotai in-memory atom is primary; localStorage is used as fallback on reload and cleared on send.
- **Message search date range**: From/To date pickers in the search filter bar. Sends `from_ts`/`to_ts` epoch ms to the Matrix `/search` endpoint. Chip shows active range with X to clear.
- **Image/video captions**: Caption text field on image and video upload — sent as a single event with the media.
- **Location sharing**: Map embed view for incoming location events + static share button. Renders `m.location` events inline with a map tile.
- **Deleted message placeholders**: Redacted `m.room.message`, `m.room.encrypted`, and `m.sticker` events render as "This message has been deleted" with reason (if provided) rather than disappearing. One-line change in the `eventRenderer` filter in `RoomTimeline.tsx`.
- **Message bookmarks / saved messages**: Right-click any message → "Bookmark" / "Remove Bookmark". A star icon in the sidebar nav opens `BookmarksPanel.tsx` — a right-side panel listing all saved messages with room name, preview text, relative timestamp, filter input, "Jump to message" deep-link, and individual remove buttons. Stored in `io.lotus.bookmarks` account data (max 500 entries); syncs across devices. Implemented in `src/app/features/bookmarks/BookmarksPanel.tsx` + `src/app/hooks/useBookmarks.ts`.
- **Message scheduling (MSC4140)**: Clock button next to send opens `ScheduleMessageModal.tsx` with a message textarea and datetime picker. Messages sent via the MSC4140 delayed events API (`org.matrix.msc4140`), confirmed supported on `matrix.lotusguild.org`. A collapsible tray above the composer lists pending scheduled messages with Cancel buttons. Utilities in `src/app/utils/scheduledMessages.ts`.
- **Richer link preview cards**: `UrlPreviewCard.tsx` renders domain-specific cards for 13 sites: YouTube (thumbnail + ▶ play overlay), Vimeo, GitHub (repo parse), Twitter/X (tweet text + media parse), Reddit (subreddit + upvotes + comments), Spotify (artwork), Twitch (LIVE badge + game), Steam, Wikipedia, Discord (server invite), npm, Stack Overflow, and IMDb (poster). Generic cards gain a favicon from Google's S2 service. Cards that produce no renderable content are suppressed.
- **File upload compression (opt-in)**: JPEG and PNG files in the upload preview show a "Compress" checkbox. When checked, a Canvas API call (`toBlob(..., 'image/jpeg', 0.82)`) compresses the image client-side. Original and compressed sizes are shown side-by-side. Compression is strictly opt-in — unchecked by default, skipped for GIF/SVG/WebP.
### Room Customization
- **Personal room name overrides**: Right-click any room in the sidebar → "Rename for me…" to set a local display name visible only to you. Other members see the original name unchanged. A small pencil icon marks rooms with a custom local name. Stored in Matrix account data (`io.lotus.room_names`). Uses `io.lotus.room_names` account data key (based on MSC4431).
- **Export room history**: Room Settings → Export tab. Supports Plain Text, JSON, and HTML formats with optional start/end date range filters. Paginates backwards via `mx.paginateEventTimeline()` with a live progress counter. E2EE-aware — events that failed decryption are skipped rather than exported as garbled ciphertext. Downloads via `Blob` + `<a download>`. Implemented in `src/app/features/room-settings/ExportRoomHistory.tsx`.
- **Room activity / mod log**: Room Settings → Activity tab. Filterable log of `m.room.member` (join/leave/kick/ban/unban/invite), `m.room.power_levels`, `m.room.name`, `m.room.topic`, `m.room.avatar`, and `m.room.server_acl` events. Human-readable descriptions, relative timestamps, type-filter dropdown, Load More pagination, and auto-paginate on mount. Implemented in `src/app/features/room-settings/RoomActivityLog.tsx`.
- **Server ACL editor**: Room Settings → Server ACL tab. Reads and writes `m.room.server_acl` state events. Editable allow and deny server lists with wildcard pattern validation (`*.example.com`). "Allow IP literal addresses" toggle. Read-only view shown to users without the required power level. Implemented in `src/app/features/room-settings/RoomServerACL.tsx`.
- **Room stats / insights**: Room Settings → Insights tab (not the default tab). Derives all statistics from the local timeline cache only; a disclaimer banner clarifies this. Shows top 5 active members (bar chart), top 5 reactions (emoji chips), media breakdown (images/videos/audio/files tiles), and a 24-hour activity heatmap (CSS bar chart). Implemented in `src/app/features/room-settings/RoomInsights.tsx`.
### Per-Message Read Receipts
Full per-message read receipt system — shows who has read each message directly in the timeline.
**Architecture:**
- `useRoomReadPositions(room)` hook — computes a `Map<eventId, userId[]>` from all joined members' `room.getEventReadUpTo()` positions. Subscribes to `RoomEvent.Receipt` for live updates (debounced at 150ms to batch burst updates from mass-read events).
- `nearestRenderableId(liveEvents, evtId)` — receipts can land on reaction/edit events that `RoomTimeline` skips (renders `null`). This walks backwards from the receipt event through the live timeline until it finds a non-reaction/non-edit event to attach to.
- `ReadPositionsContext` — React context providing the positions map from `RoomTimeline` down to all `Message` instances without prop drilling.
- `ReadReceiptAvatars` component — renders a pill-shaped row of overlapping `StackedAvatar` circles (24px, `SurfaceVariant` outline) below messages with readers. Pill uses `color.SurfaceVariant.Container` background for visibility on any wallpaper. Max 5 avatars shown + `+N` overflow count. Avatar fallback uses `colorMXID(userId)` for distinctive per-user color.
- Clicking the pill opens the **"Seen by" modal** (`EventReaders`) listing all readers with their avatar, display name, and a formatted read timestamp ("Today at 3:42 PM", "Yesterday at 10:15 AM", "May 14 at 9:00 AM"). Timestamps use `room.getReadReceiptForUserId(userId)?.data.ts` and respect the user's 24-hour clock setting.
- Authenticated media (`mxcUrlToHttp` utility) used for all avatar loads, matching the correct Lotus utility signature.
### Delivery Status Indicators
Own messages display a small status marker below the message content (when no read receipts are visible yet):
- `⟳` — message is being sent / encrypting
- `✓` — message confirmed sent (local echo)
- `✕` — message failed to send (shown in red; orange glow in TDS mode)
- Status hidden once the server confirms receipt (`status === null`) — read receipts take over at that point
### URL Preview Cards (TDS)
URL preview cards (`UrlPreviewCard`) styled for terminal mode:
- Dark transparent background with cyan border-left accent (Anduril Orange)
- Link text in cyan, hover switches to orange with glow
- Light TDS variant: off-white background with blue accent
### Reaction Chips (TDS)
Emoji reaction buttons styled for terminal mode via `button[data-reaction-key]` selector:
- Own reaction (aria-pressed=true): orange tint `rgba(255,107,0,0.12)`, orange border
- Light TDS: equivalent blue/orange variants
### DM Call Improvements
- **Incoming call ring**: DM calls trigger a ring tone with Answer/Decline UI. 30-second auto-dismiss if unanswered. Implemented in `Room.tsx` and `RoomViewHeader.tsx`.
### Room Customization
- **Room emoji prefix**: A leading emoji in a room name (e.g. 🎮 general) renders at 1.15× size in the sidebar for visual impact. Matrix room names already support Unicode — this is purely a rendering enhancement in `RoomNavItem.tsx`. All three room-name inputs (Create Room, Room Settings, "Rename for me…" dialog) now include a 😊 emoji picker button that prepends the selected emoji to the name field.
### Presence
- **Discord-style presence selector**: Clicking your avatar in the bottom-left sidebar opens a popout with five status options — Online (green), Idle (yellow), Do Not Disturb (red, broadcasts `unavailable` with `status_msg: 'dnd'`), Invisible (grey outline, broadcasts `offline`), and Auto (activity-tracking, the original behaviour). The selected status persists across reloads via the settings atom. A colored badge on the avatar reflects the current status at a glance. `usePresenceUpdater` short-circuits immediately for manual modes; full idle-timer and visibility-change logic only runs in Auto mode. Settings also exposed via `src/app/state/settings.ts` (`presenceStatus` field).
- **Custom status message**: Set a short status text (up to 64 characters) with an emoji picker, shown below your display name in member lists and presence displays. Accessible via Settings → Account → Profile. Includes an **auto-clear timer** (options: 30 minutes, 1 hour, 4 hours, 1 day, 3 days, 7 days) — after the timer expires, the status is automatically cleared by setting `status_msg: ''` via `mx.setPresence`. A character counter (shown when ≥ 56/64 chars) prevents overflow. Implemented in `src/app/features/settings/account/Profile.tsx`.
- **Presence badges on members**: Online/busy/away dots shown next to users in the room members drawer and settings members panel (`PresenceBadge` component from `src/app/components/presence/Presence.tsx`).
- **Presence avatar border ring**: A 2px colored `outline` ring on user avatars throughout the app shows presence at a glance — green (online), yellow (idle), red (DND), no ring (offline). Implemented as `PresenceRingAvatar` component (`src/app/components/presence/PresenceRingAvatar.tsx`) using `React.cloneElement` to inject `outline` + `outlineOffset` directly onto the child `Avatar` element — the ring follows the avatar's actual `border-radius` regardless of shape. Applied to: message timeline sender avatars, members drawer, @mention autocomplete, and inbox notification senders.
- **Document title unread count**: Tab title updates to `(N) Lotus Chat` for mentions, `· Lotus Chat` for unreads, `Lotus Chat` when clear.
- **Extended profile fields (MSC4133)**: Settings → Account → Profile includes Pronouns (`m.pronouns`) and Timezone (`m.tz`) fields, saved via MSC4133 `PUT /_matrix/client/unstable/uk.tcpip.msc4133/{userId}/{field}`. Both fields are displayed in user profile panels. Implemented via `src/app/hooks/useExtendedProfile.ts`.
- **User local time in profile**: When a user has `m.tz` set, their profile panel shows a clock icon, their current local time, and the timezone abbreviation (e.g. EST, JST). Updates every 60 seconds. Respects the viewer's `hour24Clock` setting. Implemented via `src/app/hooks/useLocalTime.ts`.
### UX & Composer
- **Message length counter**: A muted character counter appears just left of the send button while typing, disappearing when the composer is empty. Resets on room switch.
- **Quick emoji reactions on hover**: The 3 most-recently-used emoji reactions appear directly in the message hover toolbar (between the emoji-board button and Reply), so reacting requires a single click rather than opening the 3-dots menu first. Clicking a quick-reaction also closes any open emoji picker. Powered by `useRecentEmoji` sourced from Matrix account data.
- **In-app notification toasts**: When a message or invite notification fires and the browser window is focused, a slim TDS-styled toast card slides in from the bottom-right instead of triggering an OS notification. Card shows: 24px avatar (initials fallback), sender name in orange, truncated message body, room name, × dismiss, 4 s auto-dismiss. Clicking navigates directly to the correct room (DM or home path) or the invites inbox. OS notifications are unchanged when the window is not focused. Implemented in `src/app/features/toast/LotusToastContainer.tsx` + `src/app/state/toast.ts`.
- **Collapsible long messages**: Messages exceeding ~20 lines are auto-collapsed with a "Read more ↓" button. Click to expand inline; a "Collapse ↑" button re-folds. Threshold (in lines) configurable in Settings → Appearance. Uses CSS `max-height` + `overflow: hidden` — works correctly with code blocks and embedded media. Respects `prefers-reduced-motion`.
- **Message send animation**: Own sent messages fade and scale into the timeline (0.15 s ease-out: `scale(0.97)→scale(1)`, `opacity 0.4→1`). Incoming messages are unaffected. Respects `prefers-reduced-motion`.
- **Right-click room context menu**: Expanded sidebar room context menu — **Mute** now opens a duration submenu (15 min / 1 hr / 8 hr / 24 hr / Indefinite) with auto-restore after the selected window; **Copy Room Link** copies the `matrix.to` URL with a "Copied!" flash; **Mark as Read** marks the room read to the latest event; plus Leave Room and Room Settings shortcuts.
- **Unverified device warning**: `warnOnUnverifiedDevices` setting (default off). When enabled via Settings → General → Privacy, a warning banner appears above the composer in encrypted rooms that contain unverified devices, showing the count. Sending is never blocked — the banner is informational only. Uses the existing `useUnverifiedDeviceCount()` hook.
- **Sidebar room filter**: A search-icon input at the top of the Home and DMs sidebar tabs filters rooms by display name in real time. Clears on tab switch. Styled to match the members-drawer search bar (`size="400"`, search prefix icon).
- **DM last message preview**: Each DM row in the sidebar shows a truncated message body (48 chars) and relative timestamp (`Xm`, `Xhr`, `Yesterday`, `D MMM`) below the room name, sourced reactively from `useRoomLatestRenderedEvent`. Encrypted rooms show "Encrypted message" only on actual decryption failure.
- **Room sort order**: Sort icon in the Rooms sidebar header lets users sort non-space rooms by Recent Activity (default), A→Z, or Unread First. Persists via `homeRoomSort` setting.
- **Favorite rooms**: Right-click any room → "Add to Favorites" / "Remove from Favorites". Favorited rooms (using the standard Matrix `m.favourite` tag) appear in a collapsible "Favorites" section above the main room list on the Home tab. Syncs across devices via account data.
- **Poll creation**: Polls can be created directly from the composer — `Icons.OrderList` button opens a modal with question field, 2–10 answer options (add/remove), and Single/Multiple choice toggle. Sends a stable `m.poll.start` event. (Poll display & voting were already supported.)
- **Voice message playback speed**: `0.75×` → `1×` → `1.5×` → `2×` speed toggle pill on voice message player — cycles on click via `playbackRate` on the `<audio>` element.
- **Invite link + QR code**: Room settings → General shows a "Share Room" tile with the `matrix.to` invite URL and a QR code. The Invite modal also has a `⊞` toggle button showing a QR panel when clicked. Both use `api.qrserver.com` (added to CSP on LXC 106).
- **Private read receipts**: Settings → General → Privacy — "Private Read Receipts" toggle. When on, sends `m.read.private` instead of `m.read` so other room members can't see when you've read messages.
- **Media gallery**: A right-side drawer (photo icon in room header, Desktop only) showing Images | Videos | Files tabs. Reads already-decrypted timeline events — works in E2EE rooms. Encrypted-blob images show a lock-icon placeholder. Load More paginates backwards via `mx.paginateEventTimeline()`.
- **Knock-to-join**: When a room's join rule is `knock`, RoomIntro shows "Request to Join" (calls `mx.knockRoom()`) with "Request sent" pending state. Room admins see a "Pending Requests" section in the members drawer with Approve / Deny buttons.
- **Code syntax highlighting** (TDS mode): Fenced code blocks in messages highlight keywords (cyan), strings (green), numbers (orange), comments (italic dim), function names (purple) using inline `--lt-accent-*` CSS variables. Custom tokenizer in `syntaxHighlight.ts` — supports JS/TS/JSX/TSX, Python, Rust. Falls back to ReactPrism for other languages.
### Settings (Appearance)
- **Animated Chat Backgrounds**: Five CSS-only animated wallpapers added to the background picker — Digital Rain (two-layer vertical stripe scroll with parallax), Star Drift (three-layer radial-gradient star field drifting diagonally), Grid Pulse (neon grid lines expanding/contracting via `backgroundSize` keyframe), Aurora Flow (four radial gradient ellipses sweeping across a 200% canvas), Fireflies (three layers of glowing dots drifting). All use vanilla-extract `keyframes()` — no canvas, GPU-composited. Respects `prefers-reduced-motion: reduce` (animation stripped at call time). "Pause Background Animations" toggle in Settings → Appearance provides an in-app override. Implemented in `src/app/styles/Animations.css.ts` + `src/app/features/lotus/chatBackground.ts`.
- **Glassmorphism Sidebar**: Settings → Appearance toggle (off by default). When enabled, the left sidebar becomes semi-transparent (`background: rgba(3,5,8,0.55)`) with `backdrop-filter: blur(12px)` so chat background patterns show through as a frosted glass effect. Fix: the active chat background is mirrored onto `document.body` via `useEffect` in `SidebarNav.tsx` so the blur has content to work through (previously the sidebar was a flex sibling with nothing physically behind it). Implemented as a vanilla-extract `SidebarGlass` class applied to the `<Sidebar>` container in `SidebarNav.tsx`.
- **Night Light / Blue Light Filter**: Warm orange overlay (`rgba(255,140,0,N%)`) across the entire UI. Toggle + intensity slider (5–80%) in Settings → Appearance. `position:fixed; pointer-events:none; z-index:9998`. Persists across sessions.
### Notification Enhancements
- **Custom notification sounds**: `messageSoundId` / `inviteSoundId` settings select per-category notification sound (`notification.ogg`, `invite.ogg`, `call.ogg`, or None). Settings → Notifications expands the sound toggle with Message Sound + Invite Sound selects and ▶ preview buttons. Shared `notificationSounds.ts` module.
- **Notification quiet hours**: `quietHoursEnabled` / `quietHoursStart` / `quietHoursEnd` settings suppress all desktop notifications and sounds during a configured time window. Handles overnight spans (e.g. 23:00–08:00). Settings → Notifications: Quiet Hours card with toggle + start/end time pickers.
- **Full push rule editor**: Settings → Notifications → Advanced Push Rules section. Covers override, room, sender, and underride rule kinds. Each row has a human-readable label for built-in rules, an enable/disable toggle, and a delete button for custom rules. An add-rule form at the bottom of the room and sender sections lets users create new per-room or per-sender push rules by entering the room/user ID.
### Calls (Extended)
- **Push-to-Deafen**: Press `M` during a call to toggle speaker mute (deafen). Configurable in Settings → General → Calls alongside the PTT key. Skips editable elements; guards `e.repeat`; uses `el.ownerDocument.body` for iframe safety.
- **TDS typing indicator dots**: When Lotus Terminal mode is active, the animated typing indicator dots turn TDS orange (`var(--lt-accent-orange)`) via `color: currentColor` inheritance.
### Server Integration
- **Server support contact (MSC1929)**: Settings → Help & About displays the homeserver admin contact fetched from `/.well-known/matrix/support`. Shows the admin's Matrix ID and a link to the support page when the homeserver has configured this endpoint. Degrades gracefully when not configured (section is hidden on 404 or network error). In TDS mode the contact text and link render in `--lt-accent-cyan`. Implemented in `src/app/features/settings/about/About.tsx`.
- **Server notices**: Rooms of type `m.server_notice` (system messages from the homeserver) now render with a distinct "Server Notice" `<Chip variant="Warning">` badge in the room header and a disabled composer showing "This is a server notice room — you cannot send messages here." Previously indistinguishable from regular DMs. Badge in `src/app/features/room/RoomViewHeader.tsx`; composer guard in `src/app/features/room/RoomInput.tsx`.
### Infrastructure
- **Authenticated media**: All avatar/media loads use `mxcUrlToHttp(mx, mxcUrl, useAuthentication, w, h, 'crop')` from `../../utils/matrix` — the Lotus utility that handles MSC3916 authenticated media. (Upstream Cinny uses the SDK method with incorrect argument order for authenticated endpoints.)
- **Upstream tracking**: `git remote add upstream https://github.com/cinnyapp/cinny.git`. Merge strategy: `git fetch upstream && git merge upstream/main`. Daily check via `cinny-upstream-check.sh` on LXC 106 — notifies Matrix on new upstream commits.
- **Rolldown CJS interop — millify**: `src/app/plugins/millify.ts` uses a named import (`import { millify as millifyPlugin } from 'millify'`) instead of a default import. Rolldown's `__toESM` helper with `mode=1` sets `a.default = module_object` (not the function itself) when `hasOwnProperty` prevents the copy — calling `millifyPlugin()` would throw `(0, zc.default) is not a function`. Named import bypasses the interop entirely.
- **Sentry noise filter**: `ignoreErrors: ['Request timed out']` added to `Sentry.init` in `src/index.tsx` to suppress unhandled rejections from the matrixRTC delayed-event heartbeat (matrix-sdk) and the widget PostmessageTransport initial-load race (matrix-widget-api). Neither is actionable from client code.
- **URL preview default in encrypted rooms**: `encUrlPreview` default changed from `false` to `true` in `src/app/state/settings.ts`. A security note is shown next to the toggle in Settings → General explaining that the homeserver fetches the URL (and sees it) but not the message content.
NODE_OPTIONS=--max_old_space_size=6144 npm run build
```
## Development workflow
All code changes should be made in the local clone at `/root/code/cinny` on the dev box, then committed and pushed to `origin/lotus`. The CI/CD pipeline handles everything from there — no manual build or deploy steps needed.
<details>
<summary>PGP Public Key to verify pre-compiled tarball</summary>
> 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 on windows](https://github.com/coreybutler/nvm-windows#installation--upgrades) on Windows and [nvm](https://github.com/nvm-sh/nvm) on Linux/macOS are pretty good choices. Also recommended nodejs version is 16.15.0 LTS.
Execute the following commands to compile the app from its source code:
```sh
npm ci # Installs all dependencies
npm run build # Compiles the app into the dist/ directory
```
Pipeline (`.gitea/workflows/ci.yml` + `lotus_deploy.sh` on LXC 106):
2. Build must pass as the CI gate; quality checks are informational (`continue-on-error`)
3. A Gitea webhook fires `lotus_deploy.sh` on LXC 106, which polls the API until CI passes (up to 15 min), then pulls `origin/lotus`, runs `npm ci && npm run build`, and rsyncs to `/var/www/html/`
You can then copy the files to a webserver's webroot of your choice.
LXC 106's stored Gitea credential is **read-only** — it can only pull. Pushes must be done from the dev box with your personal credentials (entered manually, never cached).
To serve a development version of the app locally for testing, you need to use the command `npm start`.
## Deployment
### Running with Docker
Built files are served from `/var/www/html/` on LXC 106 (nginx). Config lives at `/opt/lotus-cinny/config.json` (vite copies it to `dist/`):
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:
```json
{
"defaultHomeserver": 0,
"homeserverList": ["matrix.lotusguild.org"],
"allowCustomHomeservers": false,
"gifApiKey": "<giphy_key>"
}
```
docker build -t cinny:latest .
```
## Key Custom Files
You can then run the container you've built with a command similar to this:
| File | Purpose |
|------|---------|
| `src/lotus-terminal.css.ts` | All TDS CSS tokens, global styles, light/dark variants |
| `src/lotus-boot.ts` | Boot sequence animation (runs once per session) |
| `src/app/hooks/useRoomReadPositions.ts` | Per-message read receipt position map |
| `src/app/features/room/ReadPositionsContext.ts` | React context for read positions |
| `src/app/features/room-settings/ExportRoomHistory.tsx` | Export room messages to plain text / JSON / HTML with date range filter and E2EE awareness |
| `src/app/features/room-settings/RoomActivityLog.tsx` | Filterable mod log of room state events (joins, kicks, bans, power level changes, etc.) |
| `src/app/features/room-settings/RoomServerACL.tsx` | Server ACL viewer/editor (allow/deny lists, IP literal toggle, power-level gated) |
| `src/app/features/room-settings/RoomInsights.tsx` | Room stats panel: top members bar chart, top reactions, media breakdown, 24h heatmap |
| `src/app/features/bookmarks/BookmarksPanel.tsx` | Saved messages sidebar panel with filter, jump-to-message, and remove |
| `src/app/hooks/useBookmarks.ts` | Read/write `io.lotus.bookmarks` account data for message bookmarks |
| `src/app/features/room/ScheduleMessageModal.tsx` | Schedule-message modal with datetime picker; sends via MSC4140 delayed events API |
| `src/app/utils/scheduledMessages.ts` | Utilities for MSC4140 scheduled message state and cancel endpoint |
<metaname="description"content="A Matrix client where you can enjoy the conversation using simple, elegant and secure interface protected by e2ee with the power of open source.">
<metaproperty="og:description"content="A Matrix client where you can enjoy the conversation using simple, elegant and secure interface protected by e2ee with the power of open source.">
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.