From 281aedcf1f0416cd8acb236b9e17c77e96568d33 Mon Sep 17 00:00:00 2001 From: M <91066601+mishl-dev@users.noreply.github.com> Date: Tue, 3 Mar 2026 23:53:40 +0600 Subject: [PATCH 1/4] fix(jumpTo): add missing newline at end of file (#867) --- src/equicordplugins/jumpTo/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equicordplugins/jumpTo/index.tsx b/src/equicordplugins/jumpTo/index.tsx index 36bfb3b7a..537d9c19f 100644 --- a/src/equicordplugins/jumpTo/index.tsx +++ b/src/equicordplugins/jumpTo/index.tsx @@ -129,4 +129,4 @@ export default definePlugin({ "user-context": UserMenuPatch, "message": MessageMenuPatch } -}); \ No newline at end of file +}); From 5fe5bd62a38d6f865adf92f95ea7969e0ab73a03 Mon Sep 17 00:00:00 2001 From: YashRaj <91825864+YashRajCodes@users.noreply.github.com> Date: Tue, 3 Mar 2026 23:23:56 +0530 Subject: [PATCH 2/4] ci: make ci faster by not installing chrome (#865) * ci: apparently ubuntu-latest has chrome preinstalled * ci: refactor and prefer offline cached deps --- .github/workflows/reportBrokenPlugins.yml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.github/workflows/reportBrokenPlugins.yml b/.github/workflows/reportBrokenPlugins.yml index b989f896f..117a9a70c 100644 --- a/.github/workflows/reportBrokenPlugins.yml +++ b/.github/workflows/reportBrokenPlugins.yml @@ -28,12 +28,8 @@ jobs: steps: - uses: actions/checkout@v4 - if: ${{ github.event_name == 'schedule' }} with: - ref: dev - - - uses: actions/checkout@v4 - if: ${{ github.event_name == 'workflow_dispatch' }} + ref: ${{ github.event_name == 'schedule' && 'dev' || github.ref }} - uses: pnpm/action-setup@v3 # Install pnpm using packageManager key in package.json @@ -45,13 +41,7 @@ jobs: - name: Install dependencies run: | - pnpm install --frozen-lockfile - - - name: Install Google Chrome - id: setup-chrome - uses: browser-actions/setup-chrome@82b9ce628cc5595478a9ebadc480958a36457dc2 - with: - chrome-version: stable + pnpm install --frozen-lockfile --prefer-offline - name: Build Equicord Reporter Version run: pnpm buildReporter @@ -60,7 +50,7 @@ jobs: timeout-minutes: 10 run: | export PATH="$PWD/node_modules/.bin:$PATH" - export CHROMIUM_BIN=${{ steps.setup-chrome.outputs.chrome-path }} + export CHROMIUM_BIN=/usr/bin/google-chrome esbuild scripts/generateReport.ts > dist/report.mjs From 7a546afa1f88116cbddc182b78a4385c153dffb5 Mon Sep 17 00:00:00 2001 From: nexpid Date: Tue, 3 Mar 2026 21:18:20 +0100 Subject: [PATCH 3/4] SongSpotlight: UI updates (#868) * feat(SongSpotlight): mostly UI updates! - new compact settings - css fixes - & more! * fix(SongSpotlight): dont really need the filter * feat(SongSpotlight): update the wording --- .../songSpotlight.desktop/index.tsx | 18 +-- .../songSpotlight.desktop/settings.tsx | 34 ++++++ .../songSpotlight.desktop/style.css | 23 ++-- .../songSpotlight.desktop/ui/common.tsx | 8 ++ .../ui/songs/CollapsedProfileSongs.tsx | 103 ++++++++++++++++++ .../ui/songs/ProfileSongs.tsx | 68 ++++++++++-- .../songSpotlight.desktop/ui/songs/index.tsx | 2 +- 7 files changed, 223 insertions(+), 33 deletions(-) create mode 100644 src/equicordplugins/songSpotlight.desktop/settings.tsx create mode 100644 src/equicordplugins/songSpotlight.desktop/ui/songs/CollapsedProfileSongs.tsx diff --git a/src/equicordplugins/songSpotlight.desktop/index.tsx b/src/equicordplugins/songSpotlight.desktop/index.tsx index 83ecdf203..ed8d84c12 100644 --- a/src/equicordplugins/songSpotlight.desktop/index.tsx +++ b/src/equicordplugins/songSpotlight.desktop/index.tsx @@ -6,15 +6,14 @@ import "./style.css"; -import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { EquicordDevs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin from "@utils/types"; import { useAuthorizationStore } from "./lib/stores/AuthorizationStore"; import { useSongStore } from "./lib/stores/SongStore"; import { Native } from "./service"; -import Settings from "./ui/settings"; +import settings from "./settings"; import ProfileSongs from "./ui/songs/ProfileSongs"; import WidgetSongs from "./ui/songs/WidgetSongs"; @@ -22,12 +21,7 @@ export default definePlugin({ name: "SongSpotlight", description: "Show off songs on your profile", authors: [EquicordDevs.nexpid], - settings: definePluginSettings({ - manager: { - type: OptionType.COMPONENT, - component: () => , - }, - }), + settings, patches: [ // Personal profile popout { @@ -50,7 +44,7 @@ export default definePlugin({ find: ".SIDEBAR}),nicknameIcons:", replacement: { match: /{userId:(\i)\.id}\)}\).{0,100}]}\)(?=\]\}\))/, - replace: "$&,$self.renderProfileSongs({userId:$1.id})", + replace: "$&,$self.renderProfileSongs({userId:$1.id,isSidebar:true})", }, }, // Full profile modal sections (lazy loaded) @@ -58,14 +52,14 @@ export default definePlugin({ find: ".MUTUAL_GUILDS})),", replacement: { match: /(\i).push\({text.{0,50}}\);/, - replace: "$&$1.push({text:\"Song Spotlight\",section:\"SONG_SPOTLIGHT\"});", + replace: '$&$1.push({text:"Song Spotlight",section:"SONG_SPOTLIGHT"});', }, }, { find: ".hasUnsavedChanges()&&", replacement: { match: /({user:(\i),.{0,80}return (\i===))/, - replace: "$1\"SONG_SPOTLIGHT\"?$self.renderWidgetSongs({user:$2}):$3", + replace: '$1"SONG_SPOTLIGHT"?$self.renderWidgetSongs({user:$2}):$3', }, }, ], diff --git a/src/equicordplugins/songSpotlight.desktop/settings.tsx b/src/equicordplugins/songSpotlight.desktop/settings.tsx new file mode 100644 index 000000000..2819a7adb --- /dev/null +++ b/src/equicordplugins/songSpotlight.desktop/settings.tsx @@ -0,0 +1,34 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2026 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { makeRange, OptionType } from "@utils/types"; + +import Settings from "./ui/settings"; + +export default definePluginSettings({ + collapseSongList: { + type: OptionType.BOOLEAN, + description: "Collapses the song list on user profiles to a button which opens a separate menu", + default: false, + }, + profileSongsLimit: { + type: OptionType.SLIDER, + description: "How many songs are shown when initially clicking on a user", + default: 6, + markers: makeRange(1, 6), + }, + manager: { + type: OptionType.COMPONENT, + component: () => , + }, +}, { + profileSongsLimit: { + disabled() { + return this.store.collapseSongList; + }, + }, +}); diff --git a/src/equicordplugins/songSpotlight.desktop/style.css b/src/equicordplugins/songSpotlight.desktop/style.css index 42f8cb293..bb44f9aaa 100644 --- a/src/equicordplugins/songSpotlight.desktop/style.css +++ b/src/equicordplugins/songSpotlight.desktop/style.css @@ -23,7 +23,10 @@ -webkit-line-clamp: 1; line-clamp: 1; -webkit-box-orient: vertical; + min-width: 0; + max-width: 100%; overflow: hidden; + text-overflow: ellipsis; } /* components */ @@ -137,13 +140,13 @@ --list-height: calc(var(--base-height) + 80px); --number-size: 32px; - padding: 8px; - gap: 12px; - overflow: visible; -} + display: flex; + flex-direction: column; + gap: 6px; -.user-profile-sidebar .vc-songspotlight-songs-container { - padding: 12px; + &.vc-songspotlight-songs-container-sidebar { + padding: 12px; + } } .vc-songspotlight-songs-more { @@ -194,6 +197,10 @@ } } +.user-profile-sidebar .vc-songspotlight-song-container { + padding: 8px; +} + .user-profile-popout:not(.custom-user-profile-theme) .vc-songspotlight-song-container { background: var(--opacity-16); } @@ -211,7 +218,6 @@ grid-template-columns: 1fr auto; grid-template-rows: 1fr auto; gap: 8px; - overflow: visible; border-radius: inherit; } @@ -220,7 +226,7 @@ grid-column: 1; grid-row: 1 / 3; border-radius: inherit; - overflow: visible; + min-width: 0; } .vc-songspotlight-song-cover { @@ -232,7 +238,6 @@ flex-shrink: 0; box-shadow: 0 3px 12px 0 rgb(0 0 0 / 16%); transition: 0.18s transform; - overflow: visible; &:hover { transform: scale(1.03); diff --git a/src/equicordplugins/songSpotlight.desktop/ui/common.tsx b/src/equicordplugins/songSpotlight.desktop/ui/common.tsx index 0e5454fad..eda340ce4 100644 --- a/src/equicordplugins/songSpotlight.desktop/ui/common.tsx +++ b/src/equicordplugins/songSpotlight.desktop/ui/common.tsx @@ -12,6 +12,14 @@ import { Tooltip } from "@webpack/common"; import { ComponentType, HTMLAttributes, JSX } from "react"; export const OverlayClasses = findCssClassesLazy("overlay", "inner", "outer"); +export const ContainerClasses = findCssClassesLazy( + "breadcrumb", + "innerContainer", + "icons", + "icon", + "displayCount", + "displayCountText", +); export const CardClasses = findCssClassesLazy("card", "headerText", "headerContextMenu"); export const WidgetClasses = findCssClassesLazy("tabPanelScroller", "tabListScroller"); diff --git a/src/equicordplugins/songSpotlight.desktop/ui/songs/CollapsedProfileSongs.tsx b/src/equicordplugins/songSpotlight.desktop/ui/songs/CollapsedProfileSongs.tsx new file mode 100644 index 000000000..52fa56ab1 --- /dev/null +++ b/src/equicordplugins/songSpotlight.desktop/ui/songs/CollapsedProfileSongs.tsx @@ -0,0 +1,103 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2026 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { BaseText } from "@components/index"; +import { Native } from "@equicordplugins/songSpotlight.desktop/service"; +import { CardClasses, ContainerClasses, OverlayClasses, Spinner } from "@equicordplugins/songSpotlight.desktop/ui/common"; +import { RenderSongInfo } from "@song-spotlight/api/handlers"; +import { UserData } from "@song-spotlight/api/structs"; +import { sid } from "@song-spotlight/api/util"; +import { classes } from "@utils/index"; +import { + SelectedChannelStore, + SelectedGuildStore, + useEffect, + useMemo, + UserProfileActions, + UserStore, + useState, +} from "@webpack/common"; + +import { ProfileSongsProps } from "./ProfileSongs"; + +const shownSongs = 4; + +interface CollapsedProfileSongsProps extends ProfileSongsProps { + data?: UserData; +} + +export default function CollapsedProfileSongs({ data, userId, isSidebar }: CollapsedProfileSongsProps) { + const [renders, setRenders] = useState(new Map()); + const previews = useMemo(() => data?.slice(0, shownSongs), [data]); + + useEffect(() => { + setRenders(new Map()); + if (!previews) return; + + for (const song of previews) { + Native.renderSong(song) + .catch(() => null) + .then(info => { + if (!info) return; + setRenders(renders => new Map(renders).set(sid(song), info)); + }); + } + }, [previews]); + + return ( +
{ + const user = UserStore.getUser(userId); + if (!user) return; + + const guildId = SelectedGuildStore.getGuildId(); + UserProfileActions.openUserProfileModal({ + userId, + guildId, + channelId: SelectedChannelStore.getChannelId(), + analyticsLocation: { + page: guildId ? "Guild Channel" : "DM Channel", + section: "Profile Popout", + }, + tabSection: "SONG_SPOTLIGHT", + }); + }} + > +
+ Song Spotlight +
+ {previews && data + ? previews.map((song, i) => { + const render = renders.get(sid(song)); + const extra = i === shownSongs - 1 && data.length > shownSongs; + + return ( +
+ {render?.thumbnailUrl && ( + {render.label} + )} + {extra && ( +
+ +{data.length - shownSongs} +
+ )} +
+ ); + }) + : } +
+
+
+ ); +} diff --git a/src/equicordplugins/songSpotlight.desktop/ui/songs/ProfileSongs.tsx b/src/equicordplugins/songSpotlight.desktop/ui/songs/ProfileSongs.tsx index 9bc46c95f..8137349c4 100644 --- a/src/equicordplugins/songSpotlight.desktop/ui/songs/ProfileSongs.tsx +++ b/src/equicordplugins/songSpotlight.desktop/ui/songs/ProfileSongs.tsx @@ -7,10 +7,12 @@ import { BaseText } from "@components/BaseText"; import { Flex } from "@components/Flex"; import { LinkIcon, PencilIcon } from "@components/Icons"; +import { Link } from "@components/Link"; import { listData } from "@equicordplugins/songSpotlight.desktop/lib/api"; import { useAuthorizationStore } from "@equicordplugins/songSpotlight.desktop/lib/stores/AuthorizationStore"; import { useSongStore } from "@equicordplugins/songSpotlight.desktop/lib/stores/SongStore"; import { cl } from "@equicordplugins/songSpotlight.desktop/lib/utils"; +import settings from "@equicordplugins/songSpotlight.desktop/settings"; import { CardClasses, MoreHorizontalIcon, @@ -20,40 +22,75 @@ import { import { openSettingsModal } from "@equicordplugins/songSpotlight.desktop/ui/settings"; import { sid } from "@song-spotlight/api/util"; import { copyWithToast } from "@utils/discord"; -import { classes } from "@utils/misc"; -import { ContextMenuApi, FluxDispatcher, Menu, React, Tooltip, useEffect, UserStore, useState } from "@webpack/common"; +import { classes } from "@utils/index"; +import { + ContextMenuApi, + FluxDispatcher, + Menu, + React, + Tooltip, + useEffect, + useMemo, + UserStore, + useState, +} from "@webpack/common"; import Song from "."; +import CollapsedProfileSongs from "./CollapsedProfileSongs"; -interface ProfileSongsProps { +export interface ProfileSongsProps { userId: string; + isSidebar: boolean; } -export default function ProfileSongs({ userId }: ProfileSongsProps) { +export default function ProfileSongs({ userId, isSidebar }: ProfileSongsProps) { const [failed, setFailed] = useState(false); const { isAuthorized } = useAuthorizationStore(); const { users } = useSongStore(); + const { profileSongsLimit, collapseSongList } = settings.use(); - const data = users[userId]?.data, at = users[userId]?.at; + const data = users[userId]?.data; useEffect(() => { if (isAuthorized() && !data) listData(userId).catch(() => setFailed(true)); }, [isAuthorized()]); + const [clamped, setClamped] = useState(true); + const clampedData = useMemo(() => data && (clamped ? data.slice(0, profileSongsLimit) : data), [ + data, + clamped, + profileSongsLimit, + ]); + const owned = UserStore.getCurrentUser().id === userId; - if (isAuthorized() && !data && !failed) { + const pending = isAuthorized() && !clampedData && !data && !failed; + if (!pending && !clampedData?.[0]) return null; + + if (collapseSongList) { + return ( + + ); + } else if (pending) { return ; - } else if (!data?.[0]) { - return null; } return (
- Song Spotlight + + Song Spotlight + {props => ( - {data.map((song, i) => )} + {clampedData.map((song, i) => )} + {clamped && data.length > profileSongsLimit && ( + setClamped(false)} + > + + Show {data.length - profileSongsLimit} more + + + )}
); } diff --git a/src/equicordplugins/songSpotlight.desktop/ui/songs/index.tsx b/src/equicordplugins/songSpotlight.desktop/ui/songs/index.tsx index ee42b87d6..a1fd51434 100644 --- a/src/equicordplugins/songSpotlight.desktop/ui/songs/index.tsx +++ b/src/equicordplugins/songSpotlight.desktop/ui/songs/index.tsx @@ -187,7 +187,7 @@ function SongInfo({ owned, song, render, big }: SongInfoProps) { icon={LinkIcon} action={() => copyWithToast(render.link)} /> - {owned + {!owned ? ( Date: Tue, 3 Mar 2026 18:42:42 -0500 Subject: [PATCH 4/4] Declutter: remove avatar decoration setting --- src/equicordplugins/declutter/index.tsx | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/equicordplugins/declutter/index.tsx b/src/equicordplugins/declutter/index.tsx index 661d94fe6..a88a0fcd3 100644 --- a/src/equicordplugins/declutter/index.tsx +++ b/src/equicordplugins/declutter/index.tsx @@ -36,12 +36,6 @@ export const settings = definePluginSettings({ type: OptionType.COMPONENT, component: () => SectionSeparator("User Profile"), }, - removeAvatarDecoration: { - type: OptionType.BOOLEAN, - description: "Remove avatar decorations.", - default: true, - restartNeeded: true, - }, removeNameplate: { type: OptionType.BOOLEAN, description: "Remove nameplates.", @@ -149,15 +143,6 @@ export default definePlugin({ authors: [EquicordDevs.Leon135, Devs.prism, Devs.Kyuuhachi], settings, patches: [ - { - // Avatar decoration - find: "getAvatarDecorationURL:", - replacement: { - match: /(?<=function \i\((\i)\){)(?=.{0,150}let{avatarDecoration)/, - replace: "$&return null;" - }, - predicate: () => settings.store.removeAvatarDecoration, - }, { // Nameplate find: "#{intl::AVATAR_MALLOW}",