feat(space): round out Matrix Spaces support - #292
Merged
Conversation
The space lobby carried its own parallel set of visual constants, which is why it drifted from the rest of the app: bare hex literals, legacy COLOR_* tokens and ad-hoc font sizes, all of which docs/ui-visual-spec-zh.md §0.1 forbids. - 10 bare hex literals and the legacy COLOR_* tokens now resolve through RBX_* instead. - Ad-hoc font sizes replaced by the RBX_TEXT_* presets. - The "Suggested" tag follows the badge contract in §4.2: pill radius, soft-accent fill, accent text, no border. - DrawTreeLine gained a `line_color` uniform, so the connector shader no longer hard-codes grey and takes RBX_DIVIDER from the DSL. - Header and screen surfaces unified, with a divider hairline under the header now that they share a colour. - Secondary header actions demoted to neutral buttons, leaving one primary action instead of four competing filled ones. No behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dialog sized itself with `height: Fit` while pinning its ScrollYView to 520px inside it. A scroll view needs a resolved viewport, so instead of scrolling it simply stretched: the form spilled out at full length, leaving Save/Cancel stranded in the middle of it and the Advanced section below them. - Root is a fixed height, giving the ScrollYView a real viewport, and restructured as header / scrolling body / sticky footer. - Save and Cancel moved into that footer. They act on the whole dialog, so they should not scroll away. - The sidebar was `height: Fit`, so its surface collapsed to a single row next to a much taller content area — the "General" entry appeared to float. It now fills the body, with a hairline against the content, and the selected row reads as selected: teal rail, tinted background, bold accent label. - Content became a sunken canvas holding white section cards (§4.1), replacing one long flat form divided by hairlines. - Remaining ad-hoc font sizes and legacy colour tokens moved to the RBX_TEXT_* presets and RBX palette; radio buttons follow the brand accent rather than the legacy blue. - The modal centred with `align: y: 0.1`, i.e. deliberately near the top. It is 0.5 now; the fixed height above is what lets that take effect, since a `Fill` child would claim all the space and leave nothing to centre with. DSL only; the dialog's behaviour is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Spaces could previously only be browsed. This adds the write side — joining, creating, curating, configuring and leaving them — plus the pieces of the read side that were missing. Invites - Let invited spaces through the room list filter, so a space invite reaches the invites section instead of being dropped. Joined spaces stay filtered out; the SpacesBar still owns those. - `remove_room()` now reads the live room state rather than the cached one, because a room that stops matching the filter is removed carrying a stale state. That distinction is what tells us an invite was accepted. - Accepting hands the space to its lobby (dock tab on desktop, space tab on mobile); declining just drops the entry. Creating - `CreateRoom` can emit `m.space` via `creation_content`; spaces skip the encryption initial state since they carry no messages. - Entry points: "New space" in the add menu, "New subspace" in a lobby. - A newly created top-level space is selected once the space service reports it as joined — it cannot be selected before that. Curating children - `AddRoomToSpace` / `RemoveRoomFromSpace`. Removal overwrites `m.space.child` with empty content, since state events cannot be deleted and a link without `via` is not a valid one. - New AddExistingRoomModal picks from already-joined rooms; the lobby grows a per-child "remove from space" action behind a confirmation. - Child rows resolve permission against their *direct* parent, which may be a nested subspace rather than the lobby's own space. Settings - The room settings dialog now serves spaces too: a space is a room underneath, so name/topic/avatar/addresses are identical. Only the wording differs, and the timeline-media section is hidden. - Join rules are readable and settable (invite / knock / public). Restricted rules are shown read-only: rewriting one would discard its allow-list and lock people out. - A Members tab lists joined members, sorted by power level. - `GetCreatableSpaces` reports "can add children" (m.space.child) and "can edit settings" (m.room.name) separately — different power levels, and the settings gear must follow the latter. Leaving - The lobby header gets a Leave action, available to every member rather than only administrators. Leaving used to live solely in the settings dialog, which is gated on the edit permission, so a plain member who joined a space had no way back out. Read side - Unread badges on the SpacesBar aggregate every joined room in a space, guarding against `m.space.child` cycles and double-counting. - Stop re-sorting a space's children alphabetically. The SDK already orders them per the spec (m.space.child `order`, then timestamp, then room ID); the local sort was silently discarding the ordering that a space's admins had set. - Selecting a space on desktop lands on its lobby instead of the generic Welcome tab. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Spaces could previously only be browsed. This adds the write side — joining, creating, curating, configuring and leaving them — plus the read-side pieces that were missing.
Commits
refactor(ui)refactor(ui)feat(space)The two visual refactors are separate commits per ui-visual-spec-zh.md §0.1, and each commit in the series builds on its own.
What's new
Invites. Invited spaces now pass the room list filter, so a space invite reaches the invites section instead of being dropped; joined spaces stay filtered out, since the SpacesBar owns those. Accepting hands the space to its lobby (dock tab on desktop, space tab on mobile).
Creating.
CreateRoomcan emitm.spaceviacreation_content. Entry points are "New space" in the add menu and "New subspace" in a lobby. A new top-level space is selected once the space service reports it as joined — it cannot be selected before that.Curating children.
AddRoomToSpace/RemoveRoomFromSpace, a modal to link an existing room in, and a per-child remove action behind a confirmation.Settings. The room settings dialog now serves spaces too — a space is a room underneath, so name/topic/avatar/addresses are identical; only the wording differs and the timeline-media section is hidden. Adds a join-rule control and a Members tab.
Leaving. A Leave action in the lobby header, available to every member.
Read side. Unread badges on the SpacesBar aggregate a space's rooms; desktop lands on the lobby when a space is selected.
Notes for review
A few decisions worth a second opinion:
remove_room()now reads the live room state instead of the cached one. A room that stops matching the list filter is removed carrying a stale state, and that distinction is exactly what tells us an accepted space invite has become a joined space. This touches the normal room-removal path too.m.space.childorder, then timestamp, then room ID); the local sort was silently discarding the ordering a space's admins had set. Visible consequence: subspaces no longer float above rooms, since the spec orders them together. Happy to reinstate the grouping as a secondary sort if that reads better.GetCreatableSpacesnow reports two lists, because "can add children" (m.space.child) and "can edit settings" (m.room.name) are different power levels and the settings gear must follow the latter.Testing
cargo buildandcargo test --lib(596 pass) on every commit; smoke-run shows no DSL runtime errors.Manual testing is incomplete — the join-rule control, the Members tab and the leave action have not been exercised against a live homeserver yet. The riskiest path to verify is opening settings on a restricted room and confirming it renders the read-only note rather than the three-way choice.
🤖 Generated with Claude Code