diff --git a/public/update/4.11.177.md b/public/update/4.11.177.md new file mode 100644 index 0000000..8e7166d --- /dev/null +++ b/public/update/4.11.177.md @@ -0,0 +1,22 @@ +--- +version: 4.11.177 +title: Profile colors & banners +date: 2026-08-23 +summary: Username colors now follow MSC4522 with separate dark and light theme pickers. +--- + +## Profile colors (MSC4522) + +Username colors are now stored on your Matrix profile instead of inside avatar images. In turn **will need to be updated in your Account settings**, this is now cross compatible with Element + Cinny + +- Set **separate colors for dark and light themes** in Settings → Account → Profile. +- Per-room username overrides are read from member profile data when available. + +![Paarrot](./images/usernamecolors.png) + +## Profile banners (MSC4133) + +Profile banners now live on your Matrix profile via `m.banner_url` (MSC4133), not embedded in avatar metadata. + +- Upload or remove a banner independently from your avatar. +- Other clients that support MSC4133 can read your banner directly. diff --git a/public/update/currentupdate.md b/public/update/currentupdate.md index 8e7166d..0bbd1a6 100644 --- a/public/update/currentupdate.md +++ b/public/update/currentupdate.md @@ -1,22 +1,22 @@ --- -version: 4.11.177 -title: Profile colors & banners -date: 2026-08-23 -summary: Username colors now follow MSC4522 with separate dark and light theme pickers. +version: 4.11.185 +title: Discord collectibles +date: 2026-08-24 +summary: Add Discord-style profile effects, nameplates, and avatar decorations to Paarrot profiles. --- -## Profile colors (MSC4522) +## Discord collectibles -Username colors are now stored on your Matrix profile instead of inside avatar images. In turn **will need to be updated in your Account settings**, this is now cross compatible with Element + Cinny +- Browse and apply profile effects, animated nameplates, and avatar decorations in Settings → Account → Profile. +- Collectible assets are uploaded to your Matrix profile, so other Paarrot users can see them without Discord. +- The catalog now comes from a public git, and no Discord token is required in Paarrot for the cosmetics. +- Profile-effect previews play their intro and loop animations, with a mini profile preview for each effect. -- Set **separate colors for dark and light themes** in Settings → Account → Profile. -- Per-room username overrides are read from member profile data when available. -![Paarrot](./images/usernamecolors.png) +![Discord Cosmetics 1](./images/discordcosmetics1.png) ![Discord Cosmetics 2](./images/discordcosmetics2.png) -## Profile banners (MSC4133) +## Profile presentation -Profile banners now live on your Matrix profile via `m.banner_url` (MSC4133), not embedded in avatar metadata. - -- Upload or remove a banner independently from your avatar. -- Other clients that support MSC4133 can read your banner directly. +- Avatar decorations render in messages, profile cards, and the bottom sidebar avatar. +- Nameplates to the right of messages, beneath timestamps and can be displayed behind the bottom sidebar avatar with opacity settings. +- Customize the sidebar nameplate opacity in Settings → General → Collectibles. diff --git a/public/update/images/discordcosmetics1.png b/public/update/images/discordcosmetics1.png new file mode 100644 index 0000000..53f9dd3 Binary files /dev/null and b/public/update/images/discordcosmetics1.png differ diff --git a/public/update/images/discordcosmetics2.png b/public/update/images/discordcosmetics2.png new file mode 100644 index 0000000..e984efc Binary files /dev/null and b/public/update/images/discordcosmetics2.png differ diff --git a/src/app/features/settings/account/CollectiblesSection.tsx b/src/app/features/settings/account/CollectiblesSection.tsx index 667086d..28684b3 100644 --- a/src/app/features/settings/account/CollectiblesSection.tsx +++ b/src/app/features/settings/account/CollectiblesSection.tsx @@ -338,9 +338,6 @@ export function CollectiblesSection({ collectibles, onApply }: CollectiblesSecti ); const previewDisplayName = profile.displayName; const [kind, setKind] = useState('profile_effect'); - const [hasToken, setHasToken] = useState(false); - const [tokenInput, setTokenInput] = useState(''); - const [tokenSaving, setTokenSaving] = useState(false); const [catalogLoading, setCatalogLoading] = useState(false); const [catalogError, setCatalogError] = useState(); const [items, setItems] = useState([]); @@ -352,12 +349,6 @@ export function CollectiblesSection({ collectibles, onApply }: CollectiblesSecti const desktopAvailable = isElectronCollectiblesAvailable(); - const refreshTokenState = useCallback(async () => { - if (!desktopAvailable) return; - const result = await window.electron!.discordCollectibles!.hasToken(); - setHasToken(Boolean(result?.data)); - }, [desktopAvailable]); - const loadCatalog = useCallback(async (force = false) => { if (!desktopAvailable) return; setCatalogLoading(true); @@ -376,14 +367,8 @@ export function CollectiblesSection({ collectibles, onApply }: CollectiblesSecti }, [desktopAvailable]); useEffect(() => { - refreshTokenState(); - }, [refreshTokenState]); - - useEffect(() => { - if (hasToken) { - loadCatalog(); - } - }, [hasToken, loadCatalog]); + loadCatalog(); + }, [loadCatalog]); const current = collectibles[KIND_FIELD[kind]]; @@ -416,27 +401,6 @@ export function CollectiblesSection({ collectibles, onApply }: CollectiblesSecti }); }, [filteredGroups]); - const handleSaveToken = async () => { - if (!desktopAvailable) return; - setTokenSaving(true); - setCatalogError(undefined); - try { - await window.electron!.discordCollectibles!.setToken(tokenInput); - setTokenInput(''); - await refreshTokenState(); - } catch (e) { - setCatalogError(e instanceof Error ? e.message : 'Failed to save token.'); - } - setTokenSaving(false); - }; - - const handleClearToken = async () => { - if (!desktopAvailable) return; - await window.electron!.discordCollectibles!.clearToken(); - setItems([]); - await refreshTokenState(); - }; - const handleApply = async (item: DiscordCollectibleItem) => { setApplyingId(item.id); setError(undefined); @@ -481,48 +445,24 @@ export function CollectiblesSection({ collectibles, onApply }: CollectiblesSecti return ( - Browse Discord shop collectibles live, then download and upload only what you pick to Matrix. - Your Discord token stays on this device and is only used to list the shop catalog. + Browse a community-published Discord collectibles catalog, then download and upload only what + you pick to Matrix. - {!hasToken ? ( - - Discord token (required to list the shop) - setTokenInput(e.target.value)} - /> - - {catalogError && {catalogError}} - - ) : ( - - Discord token saved on this device. - - - - )} + + + - {hasToken && ( - <> + <> {KIND_TABS.map((tab) => (