Merge branch 'main' of https://github.com/Nexulien/Nexulien
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.12.13",
|
||||
"version": "1.13.2",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
||||
1
packages/discord-types/.npmignore
Normal file
1
packages/discord-types/.npmignore
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
1
packages/discord-types/CONTRIBUTING.md
Normal file
1
packages/discord-types/CONTRIBUTING.md
Normal file
@@ -0,0 +1 @@
|
||||
Hint: https://docs.discord.food is an incredible resource and allows you to copy paste complete enums and interfaces
|
||||
30
packages/discord-types/enums/activity.ts
Normal file
30
packages/discord-types/enums/activity.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export const enum ActivityType {
|
||||
PLAYING = 0,
|
||||
STREAMING = 1,
|
||||
LISTENING = 2,
|
||||
WATCHING = 3,
|
||||
CUSTOM_STATUS = 4,
|
||||
COMPETING = 5,
|
||||
HANG_STATUS = 6
|
||||
}
|
||||
|
||||
export const enum ActivityFlags {
|
||||
INSTANCE = 1 << 0,
|
||||
JOIN = 1 << 1,
|
||||
/** @deprecated */
|
||||
SPECTATE = 1 << 2,
|
||||
/** @deprecated */
|
||||
JOIN_REQUEST = 1 << 3,
|
||||
SYNC = 1 << 4,
|
||||
PLAY = 1 << 5,
|
||||
PARTY_PRIVACY_FRIENDS = 1 << 6,
|
||||
PARTY_PRIVACY_VOICE_CHANNEL = 1 << 7,
|
||||
EMBEDDED = 1 << 8,
|
||||
CONTEXTLESS = 1 << 9
|
||||
}
|
||||
|
||||
export const enum ActivityStatusDisplayType {
|
||||
NAME = 0,
|
||||
STATE = 1,
|
||||
DETAILS = 2
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./activity";
|
||||
export * from "./channel";
|
||||
export * from "./commands";
|
||||
export * from "./messages";
|
||||
|
||||
@@ -11,3 +11,586 @@ export const enum StickerFormatType {
|
||||
LOTTIE = 3,
|
||||
GIF = 4
|
||||
}
|
||||
|
||||
export const enum MessageType {
|
||||
/**
|
||||
* A default message (see below)
|
||||
*
|
||||
* Value: 0
|
||||
* Name: DEFAULT
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
DEFAULT = 0,
|
||||
/**
|
||||
* A message sent when a user is added to a group DM or thread
|
||||
*
|
||||
* Value: 1
|
||||
* Name: RECIPIENT_ADD
|
||||
* Rendered Content: "{author} added {mentions [0] } to the {group/thread}."
|
||||
* Deletable: false
|
||||
*/
|
||||
RECIPIENT_ADD = 1,
|
||||
/**
|
||||
* A message sent when a user is removed from a group DM or thread
|
||||
*
|
||||
* Value: 2
|
||||
* Name: RECIPIENT_REMOVE
|
||||
* Rendered Content: "{author} removed {mentions [0] } from the {group/thread}."
|
||||
* Deletable: false
|
||||
*/
|
||||
RECIPIENT_REMOVE = 2,
|
||||
/**
|
||||
* A message sent when a user creates a call in a private channel
|
||||
*
|
||||
* Value: 3
|
||||
* Name: CALL
|
||||
* Rendered Content: participated ? "{author} started a call{ended ? " that lasted {duration}" : " — Join the call"}." : "You missed a call from {author} that lasted {duration}."
|
||||
* Deletable: false
|
||||
*/
|
||||
CALL = 3,
|
||||
/**
|
||||
* A message sent when a group DM or thread's name is changed
|
||||
*
|
||||
* Value: 4
|
||||
* Name: CHANNEL_NAME_CHANGE
|
||||
* Rendered Content: "{author} changed the {is_forum ? "post title" : "channel name"}: {content} "
|
||||
* Deletable: false
|
||||
*/
|
||||
CHANNEL_NAME_CHANGE = 4,
|
||||
/**
|
||||
* A message sent when a group DM's icon is changed
|
||||
*
|
||||
* Value: 5
|
||||
* Name: CHANNEL_ICON_CHANGE
|
||||
* Rendered Content: "{author} changed the channel icon."
|
||||
* Deletable: false
|
||||
*/
|
||||
CHANNEL_ICON_CHANGE = 5,
|
||||
/**
|
||||
* A message sent when a message is pinned in a channel
|
||||
*
|
||||
* Value: 6
|
||||
* Name: CHANNEL_PINNED_MESSAGE
|
||||
* Rendered Content: "{author} pinned a message to this channel."
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANNEL_PINNED_MESSAGE = 6,
|
||||
/**
|
||||
* A message sent when a user joins a guild
|
||||
*
|
||||
* Value: 7
|
||||
* Name: USER_JOIN
|
||||
* Rendered Content: See user join message type , obtained via the formula timestamp_ms % 13
|
||||
* Deletable: true
|
||||
*/
|
||||
USER_JOIN = 7,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild
|
||||
*
|
||||
* Value: 8
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}!"
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION = 8,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild to tier 1
|
||||
*
|
||||
* Value: 9
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION_TIER_1
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}! {guild} has achieved Level 1! "
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 9,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild to tier 2
|
||||
*
|
||||
* Value: 10
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION_TIER_2
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}! {guild} has achieved Level 2! "
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild to tier 3
|
||||
*
|
||||
* Value: 11
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION_TIER_3
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}! {guild} has achieved Level 3! "
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11,
|
||||
/**
|
||||
* A message sent when a news channel is followed
|
||||
*
|
||||
* Value: 12
|
||||
* Name: CHANNEL_FOLLOW_ADD
|
||||
* Rendered Content: "{author} has added {content} to this channel. Its most important updates will show up here."
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANNEL_FOLLOW_ADD = 12,
|
||||
/**
|
||||
* A message sent when a guild is disqualified from discovery
|
||||
*
|
||||
* Value: 14
|
||||
* Name: GUILD_DISCOVERY_DISQUALIFIED
|
||||
* Rendered Content: "This server has been removed from Server Discovery because it no longer passes all the requirements. Check Server Settings for more details."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_DISQUALIFIED = 14,
|
||||
/**
|
||||
* A message sent when a guild requalifies for discovery
|
||||
*
|
||||
* Value: 15
|
||||
* Name: GUILD_DISCOVERY_REQUALIFIED
|
||||
* Rendered Content: "This server is eligible for Server Discovery again and has been automatically relisted!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_REQUALIFIED = 15,
|
||||
/**
|
||||
* A message sent when a guild has failed discovery requirements for a week
|
||||
*
|
||||
* Value: 16
|
||||
* Name: GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING
|
||||
* Rendered Content: "This server has failed Discovery activity requirements for 1 week. If this server fails for 4 weeks in a row, it will be automatically removed from Discovery."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING = 16,
|
||||
/**
|
||||
* A message sent when a guild has failed discovery requirements for 3 weeks
|
||||
*
|
||||
* Value: 17
|
||||
* Name: GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING
|
||||
* Rendered Content: "This server has failed Discovery activity requirements for 3 weeks in a row. If this server fails for 1 more week, it will be removed from Discovery."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING = 17,
|
||||
/**
|
||||
* A message sent when a thread is created
|
||||
*
|
||||
* Value: 18
|
||||
* Name: THREAD_CREATED
|
||||
* Rendered Content: "{author} started a thread: {content} . See all threads."
|
||||
* Deletable: true
|
||||
*/
|
||||
THREAD_CREATED = 18,
|
||||
/**
|
||||
* A message sent when a user replies to a message
|
||||
*
|
||||
* Value: 19
|
||||
* Name: REPLY
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPLY = 19,
|
||||
/**
|
||||
* A message sent when a user uses a slash command
|
||||
*
|
||||
* Value: 20
|
||||
* Name: CHAT_INPUT_COMMAND
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CHAT_INPUT_COMMAND = 20,
|
||||
/**
|
||||
* A message sent when a thread starter message is added to a thread
|
||||
*
|
||||
* Value: 21
|
||||
* Name: THREAD_STARTER_MESSAGE
|
||||
* Rendered Content: "{referenced_message?.content}" ?? "Sorry, we couldn't load the first message in this thread"
|
||||
* Deletable: false
|
||||
*/
|
||||
THREAD_STARTER_MESSAGE = 21,
|
||||
/**
|
||||
* A message sent to remind users to invite friends to a guild
|
||||
*
|
||||
* Value: 22
|
||||
* Name: GUILD_INVITE_REMINDER
|
||||
* Rendered Content: "Wondering who to invite?\nStart by inviting anyone who can help you build the server!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INVITE_REMINDER = 22,
|
||||
/**
|
||||
* A message sent when a user uses a context menu command
|
||||
*
|
||||
* Value: 23
|
||||
* Name: CONTEXT_MENU_COMMAND
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CONTEXT_MENU_COMMAND = 23,
|
||||
/**
|
||||
* A message sent when auto moderation takes an action
|
||||
*
|
||||
* Value: 24
|
||||
* Name: AUTO_MODERATION_ACTION
|
||||
* Rendered Content: Special embed rendered from embeds[0]
|
||||
* Deletable: true 1
|
||||
*/
|
||||
AUTO_MODERATION_ACTION = 24,
|
||||
/**
|
||||
* A message sent when a user purchases or renews a role subscription
|
||||
*
|
||||
* Value: 25
|
||||
* Name: ROLE_SUBSCRIPTION_PURCHASE
|
||||
* Rendered Content: "{author} {is_renewal ? "renewed" : "joined"} {role_subscription.tier_name} and has been a subscriber of {guild} for {role_subscription.total_months_subscribed} month(?s)!"
|
||||
* Deletable: true
|
||||
*/
|
||||
ROLE_SUBSCRIPTION_PURCHASE = 25,
|
||||
/**
|
||||
* A message sent when a user is upsold to a premium interaction
|
||||
*
|
||||
* Value: 26
|
||||
* Name: INTERACTION_PREMIUM_UPSELL
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
INTERACTION_PREMIUM_UPSELL = 26,
|
||||
/**
|
||||
* A message sent when a stage channel starts
|
||||
*
|
||||
* Value: 27
|
||||
* Name: STAGE_START
|
||||
* Rendered Content: "{author} started {content} "
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_START = 27,
|
||||
/**
|
||||
* A message sent when a stage channel ends
|
||||
*
|
||||
* Value: 28
|
||||
* Name: STAGE_END
|
||||
* Rendered Content: "{author} ended {content} "
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_END = 28,
|
||||
/**
|
||||
* A message sent when a user starts speaking in a stage channel
|
||||
*
|
||||
* Value: 29
|
||||
* Name: STAGE_SPEAKER
|
||||
* Rendered Content: "{author} is now a speaker."
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_SPEAKER = 29,
|
||||
/**
|
||||
* A message sent when a user raises their hand in a stage channel
|
||||
*
|
||||
* Value: 30
|
||||
* Name: STAGE_RAISE_HAND
|
||||
* Rendered Content: "{author} requested to speak."
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_RAISE_HAND = 30,
|
||||
/**
|
||||
* A message sent when a stage channel's topic is changed
|
||||
*
|
||||
* Value: 31
|
||||
* Name: STAGE_TOPIC
|
||||
* Rendered Content: "{author} changed the Stage topic: {content} "
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_TOPIC = 31,
|
||||
/**
|
||||
* A message sent when a user purchases an application premium subscription
|
||||
*
|
||||
* Value: 32
|
||||
* Name: GUILD_APPLICATION_PREMIUM_SUBSCRIPTION
|
||||
* Rendered Content: "{author} upgraded {application ?? "a deleted application"} to premium for this server!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_APPLICATION_PREMIUM_SUBSCRIPTION = 32,
|
||||
/**
|
||||
* A message sent when a user gifts a premium (Nitro) referral
|
||||
*
|
||||
* Value: 35
|
||||
* Name: PREMIUM_REFERRAL
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_REFERRAL = 35,
|
||||
/**
|
||||
* A message sent when a user enabled lockdown for the guild
|
||||
*
|
||||
* Value: 36
|
||||
* Name: GUILD_INCIDENT_ALERT_MODE_ENABLED
|
||||
* Rendered Content: "{author} enabled security actions until {content}."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_ALERT_MODE_ENABLED = 36,
|
||||
/**
|
||||
* A message sent when a user disables lockdown for the guild
|
||||
*
|
||||
* Value: 37
|
||||
* Name: GUILD_INCIDENT_ALERT_MODE_DISABLED
|
||||
* Rendered Content: "{author} disabled security actions."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_ALERT_MODE_DISABLED = 37,
|
||||
/**
|
||||
* A message sent when a user reports a raid for the guild
|
||||
*
|
||||
* Value: 38
|
||||
* Name: GUILD_INCIDENT_REPORT_RAID
|
||||
* Rendered Content: "{author} reported a raid in {guild}."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_REPORT_RAID = 38,
|
||||
/**
|
||||
* A message sent when a user reports a false alarm for the guild
|
||||
*
|
||||
* Value: 39
|
||||
* Name: GUILD_INCIDENT_REPORT_FALSE_ALARM
|
||||
* Rendered Content: "{author} reported a false alarm in {guild}."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_REPORT_FALSE_ALARM = 39,
|
||||
/**
|
||||
* A message sent when no one sends a message in the current channel for 1 hour
|
||||
*
|
||||
* Value: 40
|
||||
* Name: GUILD_DEADCHAT_REVIVE_PROMPT
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DEADCHAT_REVIVE_PROMPT = 40,
|
||||
/**
|
||||
* A message sent when a user buys another user a gift
|
||||
*
|
||||
* Value: 41
|
||||
* Name: CUSTOM_GIFT
|
||||
* Rendered Content: Special embed rendered from embeds[0].url and gift_info
|
||||
* Deletable: true
|
||||
*/
|
||||
CUSTOM_GIFT = 41,
|
||||
/**
|
||||
* Value: 42
|
||||
* Name: GUILD_GAMING_STATS_PROMPT
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_GAMING_STATS_PROMPT = 42,
|
||||
/**
|
||||
* A message sent when a user purchases a guild product
|
||||
*
|
||||
* Value: 44
|
||||
* Name: PURCHASE_NOTIFICATION
|
||||
* Rendered Content: "{author} has purchased {purchase_notification.guild_product_purchase.product_name}!"
|
||||
* Deletable: true
|
||||
*/
|
||||
PURCHASE_NOTIFICATION = 44,
|
||||
/**
|
||||
* A message sent when a poll is finalized
|
||||
*
|
||||
* Value: 46
|
||||
* Name: POLL_RESULT
|
||||
* Rendered Content: Special embed rendered from embeds[0]
|
||||
* Deletable: true
|
||||
*/
|
||||
POLL_RESULT = 46,
|
||||
/**
|
||||
* A message sent by the Discord Updates account when a new changelog is posted
|
||||
*
|
||||
* Value: 47
|
||||
* Name: CHANGELOG
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANGELOG = 47,
|
||||
/**
|
||||
* A message sent when a Nitro promotion is triggered
|
||||
*
|
||||
* Value: 48
|
||||
* Name: NITRO_NOTIFICATION
|
||||
* Rendered Content: Special embed rendered from content
|
||||
* Deletable: true
|
||||
*/
|
||||
NITRO_NOTIFICATION = 48,
|
||||
/**
|
||||
* A message sent when a voice channel is linked to a lobby
|
||||
*
|
||||
* Value: 49
|
||||
* Name: CHANNEL_LINKED_TO_LOBBY
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANNEL_LINKED_TO_LOBBY = 49,
|
||||
/**
|
||||
* A local-only ephemeral message sent when a user is prompted to gift Nitro to a friend on their friendship anniversary
|
||||
*
|
||||
* Value: 50
|
||||
* Name: GIFTING_PROMPT
|
||||
* Rendered Content: Special embed
|
||||
* Deletable: true
|
||||
*/
|
||||
GIFTING_PROMPT = 50,
|
||||
/**
|
||||
* A local-only message sent when a user receives an in-game message NUX
|
||||
*
|
||||
* Value: 51
|
||||
* Name: IN_GAME_MESSAGE_NUX
|
||||
* Rendered Content: "{author} messaged you from {application.name}. In-game chat may not include rich messaging features such as images, polls, or apps. Learn More "
|
||||
* Deletable: true
|
||||
*/
|
||||
IN_GAME_MESSAGE_NUX = 51,
|
||||
/**
|
||||
* A message sent when a user accepts a guild join request
|
||||
*
|
||||
* Value: 52
|
||||
* Name: GUILD_JOIN_REQUEST_ACCEPT_NOTIFICATION 2
|
||||
* Rendered Content: "{join_request.user}'s application to {content} was approved! Welcome!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_JOIN_REQUEST_ACCEPT_NOTIFICATION = 52,
|
||||
/**
|
||||
* A message sent when a user rejects a guild join request
|
||||
*
|
||||
* Value: 53
|
||||
* Name: GUILD_JOIN_REQUEST_REJECT_NOTIFICATION 2
|
||||
* Rendered Content: "{join_request.user}'s application to {content} was rejected."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_JOIN_REQUEST_REJECT_NOTIFICATION = 53,
|
||||
/**
|
||||
* A message sent when a user withdraws a guild join request
|
||||
*
|
||||
* Value: 54
|
||||
* Name: GUILD_JOIN_REQUEST_WITHDRAWN_NOTIFICATION 2
|
||||
* Rendered Content: "{join_request.user}'s application to {content} has been withdrawn."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_JOIN_REQUEST_WITHDRAWN_NOTIFICATION = 54,
|
||||
/**
|
||||
* A message sent when a user upgrades to HD streaming
|
||||
*
|
||||
* Value: 55
|
||||
* Name: HD_STREAMING_UPGRADED
|
||||
* Rendered Content: "{author} activated HD Splash Potion "
|
||||
* Deletable: true
|
||||
*/
|
||||
HD_STREAMING_UPGRADED = 55,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by deleting the offending message
|
||||
*
|
||||
* Value: 58
|
||||
* Name: REPORT_TO_MOD_DELETED_MESSAGE
|
||||
* Rendered Content: "{author} deleted the message"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_DELETED_MESSAGE = 58,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by timing out the offending user
|
||||
*
|
||||
* Value: 59
|
||||
* Name: REPORT_TO_MOD_TIMEOUT_USER
|
||||
* Rendered Content: "{author} timed out {mentions [0] }"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_TIMEOUT_USER = 59,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by kicking the offending user
|
||||
*
|
||||
* Value: 60
|
||||
* Name: REPORT_TO_MOD_KICK_USER
|
||||
* Rendered Content: "{author} kicked {mentions [0] }"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_KICK_USER = 60,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by banning the offending user
|
||||
*
|
||||
* Value: 61
|
||||
* Name: REPORT_TO_MOD_BAN_USER
|
||||
* Rendered Content: "{author} banned {mentions [0] }"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_BAN_USER = 61,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report
|
||||
*
|
||||
* Value: 62
|
||||
* Name: REPORT_TO_MOD_CLOSED_REPORT
|
||||
* Rendered Content: "{author} resolved this flag"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_CLOSED_REPORT = 62,
|
||||
/**
|
||||
* A message sent when a user adds a new emoji to a guild
|
||||
*
|
||||
* Value: 63
|
||||
* Name: EMOJI_ADDED
|
||||
* Rendered Content: "{author} added a new emoji, {content} :{emoji.name}: "
|
||||
* Deletable: true
|
||||
*/
|
||||
EMOJI_ADDED = 63,
|
||||
}
|
||||
|
||||
export const enum MessageFlags {
|
||||
/**
|
||||
* Message has been published to subscribed channels (via Channel Following)
|
||||
*
|
||||
* Value: 1 << 0
|
||||
*/
|
||||
CROSSPOSTED = 1 << 0,
|
||||
/**
|
||||
* Message originated from a message in another channel (via Channel Following)
|
||||
*/
|
||||
IS_CROSSPOST = 1 << 1,
|
||||
/**
|
||||
* Embeds will not be included when serializing this message
|
||||
*/
|
||||
SUPPRESS_EMBEDS = 1 << 2,
|
||||
/**
|
||||
* Source message for this crosspost has been deleted (via Channel Following)
|
||||
*/
|
||||
SOURCE_MESSAGE_DELETED = 1 << 3,
|
||||
/**
|
||||
* Message came from the urgent message system
|
||||
*/
|
||||
URGENT = 1 << 4,
|
||||
/**
|
||||
* Message has an associated thread, with the same ID as the message
|
||||
*/
|
||||
HAS_THREAD = 1 << 5,
|
||||
/**
|
||||
* Message is only visible to the user who invoked the interaction
|
||||
*/
|
||||
EPHEMERAL = 1 << 6,
|
||||
/**
|
||||
* Message is an interaction response and the bot is "thinking"
|
||||
*/
|
||||
LOADING = 1 << 7,
|
||||
/**
|
||||
* Some roles were not mentioned and added to the thread
|
||||
*/
|
||||
FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8,
|
||||
/**
|
||||
* Message is hidden from the guild's feed
|
||||
*/
|
||||
GUILD_FEED_HIDDEN = 1 << 9,
|
||||
/**
|
||||
* Message contains a link that impersonates Discord
|
||||
*/
|
||||
SHOULD_SHOW_LINK_NOT_DISCORD_WARNING = 1 << 10,
|
||||
/**
|
||||
* Message will not trigger push and desktop notifications
|
||||
*/
|
||||
SUPPRESS_NOTIFICATIONS = 1 << 12,
|
||||
/**
|
||||
* Message's audio attachment is rendered as a voice message
|
||||
*/
|
||||
IS_VOICE_MESSAGE = 1 << 13,
|
||||
/**
|
||||
* Message has a forwarded message snapshot attached
|
||||
*/
|
||||
HAS_SNAPSHOT = 1 << 14,
|
||||
/**
|
||||
* Message contains components from version 2 of the UI kit
|
||||
*/
|
||||
IS_COMPONENTS_V2 = 1 << 15,
|
||||
/**
|
||||
* Message was triggered by the social layer integration
|
||||
*/
|
||||
SENT_BY_SOCIAL_LAYER_INTEGRATION = 1 << 16,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "@vencord/discord-types",
|
||||
"author": "Vencord Contributors",
|
||||
"private": false,
|
||||
"description": "Typescript definitions for the webpack modules of the Discord Web app",
|
||||
"version": "1.0.0",
|
||||
"license": "LGPL-3.0-or-later",
|
||||
@@ -12,8 +13,10 @@
|
||||
"directory": "packages/discord-types"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/react": "^19.0.10",
|
||||
"moment": "^2.22.2",
|
||||
"type-fest": "^4.41.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.10"
|
||||
}
|
||||
}
|
||||
|
||||
36
packages/discord-types/src/common/Activity.d.ts
vendored
Normal file
36
packages/discord-types/src/common/Activity.d.ts
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import { ActivityFlags, ActivityStatusDisplayType, ActivityType } from "../../enums";
|
||||
|
||||
export interface ActivityAssets {
|
||||
large_image?: string;
|
||||
large_text?: string;
|
||||
small_image?: string;
|
||||
small_text?: string;
|
||||
}
|
||||
|
||||
export interface ActivityButton {
|
||||
label: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface Activity {
|
||||
name: string;
|
||||
application_id: string;
|
||||
type: ActivityType;
|
||||
state?: string;
|
||||
state_url?: string;
|
||||
details?: string;
|
||||
details_url?: string;
|
||||
url?: string;
|
||||
flags: ActivityFlags;
|
||||
status_display_type?: ActivityStatusDisplayType;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
end?: number;
|
||||
};
|
||||
assets?: ActivityAssets;
|
||||
buttons?: string[];
|
||||
metadata?: {
|
||||
button_urls?: Array<string>;
|
||||
};
|
||||
}
|
||||
|
||||
1
packages/discord-types/src/common/index.d.ts
vendored
1
packages/discord-types/src/common/index.d.ts
vendored
@@ -1,3 +1,4 @@
|
||||
export * from "./Activity";
|
||||
export * from "./Application";
|
||||
export * from "./Channel";
|
||||
export * from "./Guild";
|
||||
|
||||
@@ -2,9 +2,9 @@ import { CommandOption } from './Commands';
|
||||
import { User, UserJSON } from '../User';
|
||||
import { Embed, EmbedJSON } from './Embed';
|
||||
import { DiscordRecord } from "../Record";
|
||||
import { StickerFormatType } from "../../../enums";
|
||||
import { MessageFlags, MessageType, StickerFormatType } from "../../../enums";
|
||||
|
||||
/**
|
||||
/*
|
||||
* TODO: looks like discord has moved over to Date instead of Moment;
|
||||
*/
|
||||
export class Message extends DiscordRecord {
|
||||
@@ -35,7 +35,7 @@ export class Message extends DiscordRecord {
|
||||
customRenderedContent: unknown;
|
||||
editedTimestamp: Date;
|
||||
embeds: Embed[];
|
||||
flags: number;
|
||||
flags: MessageFlags;
|
||||
giftCodes: string[];
|
||||
id: string;
|
||||
interaction: {
|
||||
@@ -100,7 +100,7 @@ export class Message extends DiscordRecord {
|
||||
stickers: unknown[];
|
||||
timestamp: moment.Moment;
|
||||
tts: boolean;
|
||||
type: number;
|
||||
type: MessageType;
|
||||
webhookId: string | undefined;
|
||||
|
||||
/**
|
||||
@@ -121,10 +121,13 @@ export class Message extends DiscordRecord {
|
||||
removeReaction(emoji: ReactionEmoji, fromCurrentUser: boolean): Message;
|
||||
|
||||
getChannelId(): string;
|
||||
hasFlag(flag: number): boolean;
|
||||
hasFlag(flag: MessageFlags): boolean;
|
||||
isCommandType(): boolean;
|
||||
isEdited(): boolean;
|
||||
isSystemDM(): boolean;
|
||||
|
||||
/** Vencord added */
|
||||
deleted?: boolean;
|
||||
}
|
||||
|
||||
/** A smaller Message object found in FluxDispatcher and elsewhere. */
|
||||
@@ -193,3 +196,9 @@ export interface MessageReaction {
|
||||
emoji: ReactionEmoji;
|
||||
me: boolean;
|
||||
}
|
||||
|
||||
// Object.keys(findByProps("REPLYABLE")).map(JSON.stringify).join("|")
|
||||
export type MessageTypeSets = Record<
|
||||
"UNDELETABLE" | "GUILD_DISCOVERY_STATUS" | "USER_MESSAGE" | "NOTIFIABLE_SYSTEM_MESSAGE" | "REPLYABLE" | "FORWARDABLE" | "REFERENCED_MESSAGE_AVAILABLE" | "AVAILABLE_IN_GUILD_FEED" | "DEADCHAT_PROMPTS" | "NON_COLLAPSIBLE" | "NON_PARSED" | "AUTOMOD_INCIDENT_ACTIONS" | "SELF_MENTIONABLE_SYSTEM" | "SCHEDULABLE",
|
||||
Set<MessageType>
|
||||
>;
|
||||
|
||||
13
packages/discord-types/src/components.d.ts
vendored
13
packages/discord-types/src/components.d.ts
vendored
@@ -161,19 +161,6 @@ export type Button = ComponentType<ButtonProps> & {
|
||||
Link: any;
|
||||
};
|
||||
|
||||
export type Switch = ComponentType<PropsWithChildren<{
|
||||
value: boolean;
|
||||
onChange(value: boolean): void;
|
||||
|
||||
disabled?: boolean;
|
||||
hideBorder?: boolean;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
|
||||
note?: ReactNode;
|
||||
tooltipNote?: ReactNode;
|
||||
}>>;
|
||||
|
||||
export type CheckboxAligns = {
|
||||
CENTER: "center";
|
||||
TOP: "top";
|
||||
|
||||
8
packages/discord-types/src/utils.d.ts
vendored
8
packages/discord-types/src/utils.d.ts
vendored
@@ -6,13 +6,15 @@ import type { FluxEvents } from "./fluxEvents";
|
||||
|
||||
export { FluxEvents };
|
||||
|
||||
type FluxEventsAutoComplete = LiteralUnion<FluxEvents, string>;
|
||||
|
||||
export interface FluxDispatcher {
|
||||
_actionHandlers: any;
|
||||
_subscriptions: any;
|
||||
dispatch(event: { [key: string]: unknown; type: FluxEvents; }): Promise<void>;
|
||||
dispatch(event: { [key: string]: unknown; type: FluxEventsAutoComplete; }): Promise<void>;
|
||||
isDispatching(): boolean;
|
||||
subscribe(event: FluxEvents, callback: (data: any) => void): void;
|
||||
unsubscribe(event: FluxEvents, callback: (data: any) => void): void;
|
||||
subscribe(event: FluxEventsAutoComplete, callback: (data: any) => void): void;
|
||||
unsubscribe(event: FluxEventsAutoComplete, callback: (data: any) => void): void;
|
||||
wait(callback: () => void): void;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@vencord/types",
|
||||
"private": false,
|
||||
"version": "1.11.5",
|
||||
"version": "1.13.2",
|
||||
"description": "",
|
||||
"types": "index.d.ts",
|
||||
"scripts": {
|
||||
@@ -19,9 +19,14 @@
|
||||
"dependencies": {
|
||||
"@types/lodash": "4.17.15",
|
||||
"@types/node": "^22.13.4",
|
||||
"@types/react": "18.3.1",
|
||||
"@types/react-dom": "18.3.1",
|
||||
"standalone-electron-types": "^34.2.0",
|
||||
"@vencord/discord-types": "^1.0.0",
|
||||
"highlight.js": "11.11.1",
|
||||
"moment": "^2.22.2",
|
||||
"ts-pattern": "^5.6.0",
|
||||
"type-fest": "^4.35.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "18.3.1",
|
||||
"@types/react-dom": "18.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
26
pnpm-lock.yaml
generated
26
pnpm-lock.yaml
generated
@@ -195,9 +195,18 @@ importers:
|
||||
'@types/react-dom':
|
||||
specifier: 18.3.1
|
||||
version: 18.3.1
|
||||
standalone-electron-types:
|
||||
specifier: ^34.2.0
|
||||
version: 34.2.0
|
||||
'@vencord/discord-types':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0(@types/react@18.3.1)
|
||||
highlight.js:
|
||||
specifier: 11.11.1
|
||||
version: 11.11.1
|
||||
moment:
|
||||
specifier: ^2.22.2
|
||||
version: 2.30.1
|
||||
ts-pattern:
|
||||
specifier: ^5.6.0
|
||||
version: 5.6.2
|
||||
type-fest:
|
||||
specifier: ^4.35.0
|
||||
version: 4.38.0
|
||||
@@ -742,6 +751,11 @@ packages:
|
||||
'@vap/shiki@0.10.5':
|
||||
resolution: {integrity: sha512-5BHVGvQT8qonbLSASon5aQFQ18OZU4FxSl9tLSj6oJ0sap3KdMbYcfGq25M9zFZR1g1dJN7fgjmZXBIS5omIdw==}
|
||||
|
||||
'@vencord/discord-types@1.0.0':
|
||||
resolution: {integrity: sha512-idtijKdbjiqo8k+4mo5Aq9zrQYwr2u11VmiRasaf+8+cGC5bYsMTIZl7zKLF3VdYx2a0ICjxgZtcfa1U3mv1Yg==}
|
||||
peerDependencies:
|
||||
'@types/react': ^19.0.10
|
||||
|
||||
abort-controller@3.0.0:
|
||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||
engines: {node: '>=6.5'}
|
||||
@@ -3499,6 +3513,12 @@ snapshots:
|
||||
vscode-oniguruma: 1.7.0
|
||||
vscode-textmate: 5.2.0
|
||||
|
||||
'@vencord/discord-types@1.0.0(@types/react@18.3.1)':
|
||||
dependencies:
|
||||
'@types/react': 18.3.1
|
||||
moment: 2.30.1
|
||||
type-fest: 4.41.0
|
||||
|
||||
abort-controller@3.0.0:
|
||||
dependencies:
|
||||
event-target-shim: 5.0.1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.nx-error-card {
|
||||
.vc-error-card {
|
||||
border: 1px solid var(--border-subtle);
|
||||
background-color: var(--background-feedback-critical);
|
||||
padding: 24px;
|
||||
|
||||
@@ -23,7 +23,7 @@ import type { HTMLProps } from "react";
|
||||
|
||||
export function ErrorCard(props: React.PropsWithChildren<HTMLProps<HTMLDivElement>>) {
|
||||
return (
|
||||
<div {...props} className={classes(props.className, "nx-error-card")}>
|
||||
<div {...props} className={classes(props.className, "vc-error-card")}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
|
||||
5
src/components/FormDivider.css
Normal file
5
src/components/FormDivider.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.vc-form-divider {
|
||||
border-top: thin solid var(--border-subtle);
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
}
|
||||
13
src/components/FormDivider.tsx
Normal file
13
src/components/FormDivider.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./FormDivider.css";
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
|
||||
export function FormDivider({ className }: { className?: string; }) {
|
||||
return <div className={classes("vc-form-divider", className)} />;
|
||||
}
|
||||
29
src/components/FormSwitch.css
Normal file
29
src/components/FormSwitch.css
Normal file
@@ -0,0 +1,29 @@
|
||||
.vc-form-switch-wrapper {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.vc-form-switch {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
> :last-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.vc-form-switch-disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.vc-form-switch-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.vc-form-switch-border {
|
||||
margin-top: 20px;
|
||||
}
|
||||
46
src/components/FormSwitch.tsx
Normal file
46
src/components/FormSwitch.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./FormSwitch.css";
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { Text } from "@webpack/common";
|
||||
import type { PropsWithChildren, ReactNode } from "react";
|
||||
|
||||
import { FormDivider } from "./FormDivider";
|
||||
import { Switch } from "./Switch";
|
||||
|
||||
export interface FormSwitchProps {
|
||||
title: ReactNode;
|
||||
description?: ReactNode;
|
||||
value: boolean;
|
||||
onChange(value: boolean): void;
|
||||
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
hideBorder?: boolean;
|
||||
}
|
||||
|
||||
export function FormSwitch({ onChange, title, value, description, disabled, className, hideBorder }: FormSwitchProps) {
|
||||
return (
|
||||
<div className="vc-form-switch-wrapper">
|
||||
<div className={classes("vc-form-switch", className, disabled && "vc-form-switch-disabled")}>
|
||||
<div className={"vc-form-switch-text"}>
|
||||
<Text variant="text-md/medium">{title}</Text>
|
||||
{description && <Text variant="text-sm/normal">{description}</Text>}
|
||||
</div>
|
||||
|
||||
<Switch checked={value} onChange={onChange} disabled={disabled} />
|
||||
</div>
|
||||
{!hideBorder && <FormDivider className="vc-form-switch-border" />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** Compatibility with Discord's old FormSwitch */
|
||||
export function FormSwitchCompat(props: PropsWithChildren<any>) {
|
||||
return <FormSwitch {...props} title={props.children ?? ""} description={props.note} />;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ type IconProps = JSX.IntrinsicElements["svg"];
|
||||
function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren<BaseIconProps>) {
|
||||
return (
|
||||
<svg
|
||||
className={classes(className, "nx-icon")}
|
||||
className={classes(className, "vc-icon")}
|
||||
role="img"
|
||||
width={width}
|
||||
height={height}
|
||||
@@ -51,7 +51,7 @@ export function LinkIcon({ height = 24, width = 24, className }: IconProps) {
|
||||
<Icon
|
||||
height={height}
|
||||
width={width}
|
||||
className={classes(className, "nx-link-icon")}
|
||||
className={classes(className, "vc-link-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="none" fillRule="evenodd">
|
||||
@@ -69,7 +69,7 @@ export function CopyIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-copy-icon")}
|
||||
className={classes(props.className, "vc-copy-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="currentColor">
|
||||
@@ -88,7 +88,7 @@ export function OpenExternalIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-open-external-icon")}
|
||||
className={classes(props.className, "vc-open-external-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<polygon
|
||||
@@ -104,7 +104,7 @@ export function ImageIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-image-icon")}
|
||||
className={classes(props.className, "vc-image-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path fill="currentColor" d="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" />
|
||||
@@ -116,7 +116,7 @@ export function InfoIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-info-icon")}
|
||||
className={classes(props.className, "vc-info-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -133,7 +133,7 @@ export function OwnerCrownIcon(props: IconProps) {
|
||||
<Icon
|
||||
aria-label={getIntlMessage("GUILD_OWNER")}
|
||||
{...props}
|
||||
className={classes(props.className, "nx-owner-crown-icon")}
|
||||
className={classes(props.className, "vc-owner-crown-icon")}
|
||||
role="img"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
@@ -154,7 +154,7 @@ export function ScreenshareIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-screenshare-icon")}
|
||||
className={classes(props.className, "vc-screenshare-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -169,7 +169,7 @@ export function ImageVisible(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-image-visible")}
|
||||
className={classes(props.className, "vc-image-visible")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path fill="currentColor" d="M5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h14q.825 0 1.413.587Q21 4.175 21 5v14q0 .825-.587 1.413Q19.825 21 19 21Zm0-2h14V5H5v14Zm1-2h12l-3.75-5-3 4L9 13Zm-1 2V5v14Z" />
|
||||
@@ -181,7 +181,7 @@ export function ImageInvisible(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-image-invisible")}
|
||||
className={classes(props.className, "vc-image-invisible")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path fill="currentColor" d="m21 18.15-2-2V5H7.85l-2-2H19q.825 0 1.413.587Q21 4.175 21 5Zm-1.2 4.45L18.2 21H5q-.825 0-1.413-.587Q3 19.825 3 19V5.8L1.4 4.2l1.4-1.4 18.4 18.4ZM6 17l3-4 2.25 3 .825-1.1L5 7.825V19h11.175l-2-2Zm7.425-6.425ZM10.6 13.4Z" />
|
||||
@@ -193,7 +193,7 @@ export function Microphone(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-microphone")}
|
||||
className={classes(props.className, "vc-microphone")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path fillRule="evenodd" clipRule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V21H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1ZM12 4C11.2 4 11 4.66667 11 5V11C11 11.3333 11.2 12 12 12C12.8 12 13 11.3333 13 11V5C13 4.66667 12.8 4 12 4Z" fill="currentColor" />
|
||||
@@ -206,7 +206,7 @@ export function CogWheel(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-cog-wheel")}
|
||||
className={classes(props.className, "vc-cog-wheel")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -223,7 +223,7 @@ export function ReplyIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-reply-icon")}
|
||||
className={classes(props.className, "vc-reply-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -238,7 +238,7 @@ export function DeleteIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-delete-icon")}
|
||||
className={classes(props.className, "vc-delete-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -257,7 +257,7 @@ export function PlusIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-plus-icon")}
|
||||
className={classes(props.className, "vc-plus-icon")}
|
||||
viewBox="0 0 18 18"
|
||||
>
|
||||
<polygon
|
||||
@@ -273,7 +273,7 @@ export function NoEntrySignIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-no-entry-sign-icon")}
|
||||
className={classes(props.className, "vc-no-entry-sign-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -292,7 +292,7 @@ export function SafetyIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-safety-icon")}
|
||||
className={classes(props.className, "vc-safety-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -310,7 +310,7 @@ export function NotesIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-notes-icon")}
|
||||
className={classes(props.className, "vc-notes-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -331,7 +331,7 @@ export function FolderIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-folder-icon")}
|
||||
className={classes(props.className, "vc-folder-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -346,7 +346,7 @@ export function LogIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-log-icon")}
|
||||
className={classes(props.className, "vc-log-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -363,7 +363,7 @@ export function RestartIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-restart-icon")}
|
||||
className={classes(props.className, "vc-restart-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -378,7 +378,7 @@ export function PaintbrushIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-paintbrush-icon")}
|
||||
className={classes(props.className, "vc-paintbrush-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
@@ -395,7 +395,7 @@ export function PencilIcon(props: IconProps) {
|
||||
return (
|
||||
<Icon
|
||||
{...props}
|
||||
className={classes(props.className, "nx-pencil-icon")}
|
||||
className={classes(props.className, "vc-pencil-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Before you go complaining that this component is unecessary,
|
||||
* This code was made *before* Vencord updated their UI, and it's
|
||||
* been used in so many places that deprecating it is not worth
|
||||
* the hastle - also cry about it.
|
||||
*/
|
||||
|
||||
import "@components/NxCard.css";
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { Text } from "@webpack/common";
|
||||
|
||||
export function NxCard({ children, className = "", ...props }) {
|
||||
return (
|
||||
<div className={`${"nx-card"} ${className}`} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NxCardTitle({ children, className = "", ...props }) {
|
||||
return (
|
||||
<Text variant="heading-md/bold" className={classes("nx-card-title", className)} {...props}>{children}</Text>
|
||||
);
|
||||
}
|
||||
@@ -6,6 +6,15 @@
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.nx-card-small {
|
||||
padding: 12px !important;
|
||||
}
|
||||
|
||||
.nx-card-grand {
|
||||
background-color: var(--background-base-lower) !important;
|
||||
box-shadow: var(--elevation-low);
|
||||
}
|
||||
|
||||
.nx-card-warning {
|
||||
background-color: var(--background-feedback-warning) !important;
|
||||
}
|
||||
@@ -28,7 +37,7 @@
|
||||
background-color: unset !important;
|
||||
}
|
||||
|
||||
.nx-card-title {
|
||||
.nx-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
@@ -39,3 +48,11 @@
|
||||
line-height: 2;
|
||||
margin-top: -8px;
|
||||
}
|
||||
|
||||
.nx-text {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.nx-text-small {
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
61
src/components/NxComponents.tsx
Normal file
61
src/components/NxComponents.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "@components/NxComponents.css";
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { Text } from "@webpack/common";
|
||||
import { HTMLProps, PropsWithChildren } from "react";
|
||||
|
||||
|
||||
|
||||
interface NxCardProps extends PropsWithChildren<Omit<HTMLProps<HTMLDivElement>, "size">> {
|
||||
variant?: "default" | "grand" | "warning" | "help" | "positive" | "danger" | "special";
|
||||
size?: "small" | "medium";
|
||||
}
|
||||
|
||||
export function NxCard(props: NxCardProps) {
|
||||
props.variant ??= "default";
|
||||
props.size ??= "medium";
|
||||
return (
|
||||
<div {...props} className={classes(props.className, "nx-card", props.size !== "medium" && "nx-card-small", props.variant !== "default" && `nx-card-${props.variant}`)}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function NxTitle(props: PropsWithChildren<HTMLProps<HTMLElement>>) {
|
||||
return (
|
||||
<Text variant="heading-md/bold" {...props} className={classes(props.className, "nx-title")}>{props.children}</Text>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
interface NxTextProps extends PropsWithChildren<Omit<HTMLProps<HTMLElement>, "size">> {
|
||||
size?: "small" | "medium";
|
||||
}
|
||||
|
||||
export function NxText(props: NxTextProps) {
|
||||
props.size ??= "medium";
|
||||
return (
|
||||
<Text {...props} variant={props.size === "medium" ? "text-md/normal" : "text-sm/normal"} className={classes(props.className, "nx-text", props.size === "small" && "nx-text-small")}>{props.children}</Text>
|
||||
);
|
||||
}
|
||||
@@ -9,9 +9,11 @@ export * from "./CodeBlock";
|
||||
export { default as ErrorBoundary } from "./ErrorBoundary";
|
||||
export * from "./ErrorCard";
|
||||
export * from "./Flex";
|
||||
export * from "./FormDivider";
|
||||
export * from "./FormSwitch";
|
||||
export * from "./Grid";
|
||||
export * from "./Heart";
|
||||
export * from "./Icons";
|
||||
export * from "./Link";
|
||||
export * from "./settings";
|
||||
|
||||
export * from "./Switch";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.nx-addon-card {
|
||||
.vc-addon-card {
|
||||
background-color: var(--background-secondary-alt);
|
||||
color: var(--interactive-active);
|
||||
border-radius: 8px;
|
||||
@@ -12,27 +12,27 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.visual-refresh .nx-addon-card {
|
||||
.visual-refresh .vc-addon-card {
|
||||
background-color: var(--card-primary-bg);
|
||||
border: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.nx-addon-card-disabled {
|
||||
.vc-addon-card-disabled {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.nx-addon-card:hover {
|
||||
.vc-addon-card:hover {
|
||||
background-color: var(--background-tertiary);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--elevation-high);
|
||||
}
|
||||
|
||||
.visual-refresh .nx-addon-card:hover {
|
||||
.visual-refresh .vc-addon-card:hover {
|
||||
/* same as non-hover, here to overwrite the non-refresh hover background */
|
||||
background-color: var(--card-primary-bg);
|
||||
}
|
||||
|
||||
.nx-addon-header {
|
||||
.vc-addon-header {
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
@@ -42,7 +42,7 @@
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.nx-addon-note {
|
||||
.vc-addon-note {
|
||||
height: 36px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -54,11 +54,11 @@
|
||||
box-orient: vertical;
|
||||
}
|
||||
|
||||
.nx-addon-name-author {
|
||||
.vc-addon-name-author {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nx-addon-name {
|
||||
.vc-addon-name {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
@@ -66,22 +66,22 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nx-addon-author {
|
||||
.vc-addon-author {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.nx-addon-author::before {
|
||||
.vc-addon-author::before {
|
||||
content: "by ";
|
||||
}
|
||||
|
||||
.nx-addon-title-container {
|
||||
.vc-addon-title-container {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 1.25em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nx-addon-title {
|
||||
.vc-addon-title {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
@@ -102,7 +102,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.nx-addon-title:hover {
|
||||
.vc-addon-title:hover {
|
||||
overflow: visible;
|
||||
animation: vc-addon-title var(--duration) linear infinite;
|
||||
}
|
||||
@@ -20,11 +20,11 @@ import "./AddonCard.css";
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { AddonBadge } from "@components/settings/PluginBadge";
|
||||
import { Switch } from "@components/settings/Switch";
|
||||
import { Switch } from "@components/Switch";
|
||||
import { Text, useRef } from "@webpack/common";
|
||||
import type { MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
const cl = classNameFactory("nx-addon-");
|
||||
const cl = classNameFactory("vc-addon-");
|
||||
|
||||
interface Props {
|
||||
name: ReactNode;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
export function AddonBadge({ text, color }) {
|
||||
return (
|
||||
<div className="nx-addon-badge" style={{
|
||||
<div className="vc-addon-badge" style={{
|
||||
backgroundColor: color,
|
||||
justifySelf: "flex-end",
|
||||
marginLeft: "auto"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.nx-settings-quickActions-container {
|
||||
.vc-settings-quickActions-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto auto;
|
||||
@@ -6,7 +6,7 @@
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.nx-settings-quickActions-title {
|
||||
.vc-settings-quickActions-title {
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -14,36 +14,36 @@
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.nx-settings-quickActions-containerButtons-2 {
|
||||
.vc-settings-quickActions-containerButtons-2 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.nx-settings-quickActions-containerButtons-3 {
|
||||
.vc-settings-quickActions-containerButtons-3 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.nx-settings-quickActions-button {
|
||||
.vc-settings-quickActions-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.nx-settings-quickActions-button:focus-visible {
|
||||
.vc-settings-quickActions-button:focus-visible {
|
||||
outline: 2px solid var(--focus-primary);
|
||||
outline-offset: 2px;
|
||||
transition-duration: 0s;
|
||||
}
|
||||
|
||||
.nx-settings-quickActions-img {
|
||||
.vc-settings-quickActions-img {
|
||||
width: 24px !important;
|
||||
height: 24px !important;
|
||||
}
|
||||
|
||||
.nx-settings-quickActions-info-button {
|
||||
.vc-settings-quickActions-info-button {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
padding: 0;
|
||||
@@ -54,17 +54,17 @@
|
||||
margin-top: -2px !important;
|
||||
}
|
||||
|
||||
.nx-settings-quickActions-info-button:hover {
|
||||
.vc-settings-quickActions-info-button:hover {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.nx-settings-quickActions-info-button:focus-visible {
|
||||
.vc-settings-quickActions-info-button:focus-visible {
|
||||
border-radius: 100%;
|
||||
outline: 2px solid var(--focus-primary);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.nx-settings-quickActions-help {
|
||||
.vc-settings-quickActions-help {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
@@ -8,7 +8,7 @@ import "./QuickAction.css";
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { InfoIcon } from "@components/Icons";
|
||||
import { NxCard, NxCardTitle } from "@components/NxCard";
|
||||
import { NxCard, NxText, NxTitle } from "@components/NxComponents";
|
||||
import { openInviteModal } from "@utils/discord";
|
||||
import { classes } from "@utils/misc";
|
||||
import { closeAllModals } from "@utils/modal";
|
||||
@@ -16,7 +16,7 @@ import { findByPropsLazy } from "@webpack";
|
||||
import { Alerts, Button, FluxDispatcher, GuildStore, NavigationRouter } from "@webpack/common";
|
||||
import type { ComponentType, PropsWithChildren, ReactNode } from "react";
|
||||
|
||||
const cl = classNameFactory("nx-settings-quickActions-");
|
||||
const cl = classNameFactory("vc-settings-quickActions-");
|
||||
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
|
||||
|
||||
export interface QuickActionProps {
|
||||
@@ -46,7 +46,7 @@ export function QuickAction(props: QuickActionProps) {
|
||||
export function QuickActionContainer({ title, children, columns = "3" }: PropsWithChildren<QuickActionContainerProps>) {
|
||||
return (
|
||||
<NxCard className={cl("container")}>
|
||||
<NxCardTitle className={cl("title")}>
|
||||
<NxTitle className={cl("title")}>
|
||||
{title}
|
||||
<button
|
||||
role="switch"
|
||||
@@ -59,8 +59,8 @@ export function QuickActionContainer({ title, children, columns = "3" }: PropsWi
|
||||
<img height="64px" width="64px" src="https://cdn.discordapp.com/emojis/1348781960453161011.gif" draggable="false"></img>
|
||||
<p>No one's around to help.</p>
|
||||
</div>
|
||||
<NxCard className="nx-card-help">
|
||||
If you're looking for actual help, please go ask in our Discord server (not Vencord's)! We'll always be there to help you out.
|
||||
<NxCard variant="help">
|
||||
<NxText>If you're looking for actual help, please go ask in our Discord server (not Vencord's)! We'll always be there to help you out.</NxText>
|
||||
</NxCard>
|
||||
</>
|
||||
),
|
||||
@@ -84,7 +84,7 @@ export function QuickActionContainer({ title, children, columns = "3" }: PropsWi
|
||||
>
|
||||
<InfoIcon />
|
||||
</button>
|
||||
</NxCardTitle>
|
||||
</NxTitle>
|
||||
<span className={cl("containerButtons-" + columns)}>{children}</span>
|
||||
</NxCard>
|
||||
);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
.nx-donate-button {
|
||||
.vc-donate-button {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.nx-donate-button .vc-heart-icon {
|
||||
.vc-donate-button .vc-heart-icon {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.nx-donate-button:hover .vc-heart-icon {
|
||||
.vc-donate-button:hover .vc-heart-icon {
|
||||
transform: scale(1.1);
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nx-special-card-special {
|
||||
.vc-special-card-special {
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
background-size: cover !important;
|
||||
@@ -21,12 +21,12 @@
|
||||
background-origin: border-box !important;
|
||||
}
|
||||
|
||||
.nx-special-card-flex {
|
||||
.vc-special-card-flex {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.nx-special-card-flex-main {
|
||||
.vc-special-card-flex-main {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -35,25 +35,25 @@
|
||||
text-shadow: 2px 2px 2px #70e4;
|
||||
}
|
||||
|
||||
.nx-special-title {
|
||||
.vc-special-title {
|
||||
color: white;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nx-special-text {
|
||||
.vc-special-text {
|
||||
color: white;
|
||||
font-size: 1em;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.nx-special-seperator {
|
||||
.vc-special-seperator {
|
||||
margin-top: 0.75em;
|
||||
border-top: 1px solid white;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.nx-special-hyperlink > .nx-special-hyperlink-text {
|
||||
.vc-special-hyperlink > .vc-special-hyperlink-text {
|
||||
color: white;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
@@ -62,7 +62,7 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nx-special-hyperlink {
|
||||
.vc-special-hyperlink {
|
||||
margin-top: 1em;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.nx-special-image-container {
|
||||
.vc-special-image-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -80,6 +80,6 @@
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.nx-special-image {
|
||||
.vc-special-image {
|
||||
width: 80%;
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import { classNameFactory } from "@api/Styles";
|
||||
import { Card, Forms, React } from "@webpack/common";
|
||||
import type { PropsWithChildren } from "react";
|
||||
|
||||
const cl = classNameFactory("nx-special-");
|
||||
const cl = classNameFactory("vc-special-");
|
||||
|
||||
interface StyledCardProps {
|
||||
title: string;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
.nx-settings-header {
|
||||
.vc-settings-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 128px;
|
||||
@@ -20,13 +20,13 @@
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.nx-settings-header-minimal > div > .nx-settings-logo-container {
|
||||
.vc-settings-header-minimal > div > .vc-settings-logo-container {
|
||||
width: 100%;
|
||||
height: 20%;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
@keyframes nx-settings-logo-boioioing {
|
||||
@keyframes vc-settings-logo-boioioing {
|
||||
0% {
|
||||
transform: scale(1) scaleX(1) scaleY(1);
|
||||
}
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.nx-settings-logo {
|
||||
.vc-settings-logo {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
@@ -56,33 +56,33 @@
|
||||
|
||||
.nx-mascot {
|
||||
/* When adjusting make sure image reaches out of card top for ~16px */
|
||||
transform: scale(2) translateX(-24px) translateY(-8px);
|
||||
transform: scale(2) translateX(-24px) translateY(-4px);
|
||||
}
|
||||
|
||||
.nx-settings-header > div {
|
||||
.vc-settings-header > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.nx-settings-header-minimal > div {
|
||||
.vc-settings-header-minimal > div {
|
||||
display: flex !important;
|
||||
flex-direction: row;
|
||||
min-width: 100% !important;
|
||||
max-height: 30px;
|
||||
}
|
||||
|
||||
.nx-settings-header-minimal .nx-settings-logo {
|
||||
.vc-settings-header-minimal .vc-settings-logo {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
.nx-settings-buttonRow {
|
||||
.vc-settings-buttonRow {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.nx-settings-buttonRow-minimal {
|
||||
.vc-settings-buttonRow-minimal {
|
||||
display: inline-flex;
|
||||
min-width: fit-content !important;
|
||||
max-height: fit-content;
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export * from "../Switch";
|
||||
export * from "./AddonCard";
|
||||
export * from "./DonateButton";
|
||||
export * from "./PluginBadge";
|
||||
export * from "./QuickAction";
|
||||
export * from "./SpecialCard";
|
||||
export * from "./Switch";
|
||||
export * from "./tabs";
|
||||
|
||||
@@ -1,10 +1,30 @@
|
||||
:root {
|
||||
--nx-green: #0f9;
|
||||
--nx-purple: #70e;
|
||||
--nx-vc-pink: #d3869b;
|
||||
--nx-green-background: #00ff9914;
|
||||
--nx-purple-background: #7700ee14;
|
||||
--nx-vc-pink-background: #d3869b14;
|
||||
}
|
||||
|
||||
.nx-removeSwitchDivider div[class*="divider"] {
|
||||
display: none;
|
||||
.nx-code {
|
||||
border-radius: 4px;
|
||||
font-size: 85%;
|
||||
height: auto;
|
||||
margin: -.2em 0;
|
||||
padding: 0 .2em;
|
||||
text-indent: 0;
|
||||
white-space: pre-wrap;
|
||||
background: var(--background-base-lowest);
|
||||
}
|
||||
|
||||
.nx-code-new {
|
||||
border-radius: 4px;
|
||||
background: var(--background-code);
|
||||
border: 1px solid var(--border-normal);
|
||||
font-size: .875rem;
|
||||
line-height: 1.125rem;
|
||||
text-indent: 0;
|
||||
white-space: pre-wrap;
|
||||
font-family: var(--font-code);
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import type { ComponentType, PropsWithChildren } from "react";
|
||||
|
||||
export function SettingsTab({ title, children }: PropsWithChildren<{ title: string; }>) {
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<section>
|
||||
<Text
|
||||
variant="heading-lg/semibold"
|
||||
tag="h2"
|
||||
@@ -35,7 +35,7 @@ export function SettingsTab({ title, children }: PropsWithChildren<{ title: stri
|
||||
</Text>
|
||||
|
||||
{children}
|
||||
</Forms.FormSection>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,18 +67,18 @@ function NotificationsTab() {
|
||||
<TabBar
|
||||
type="top"
|
||||
look="brand"
|
||||
className={classes("nx-settings-tab-bar", Margins.top16)}
|
||||
className={classes("nx-notifications-tab-bar", Margins.top16)}
|
||||
selectedItem={currentTab}
|
||||
onItemSelect={setCurrentTab}
|
||||
>
|
||||
<TabBar.Item
|
||||
className="nx-settings-tab-bar-item"
|
||||
className="nx-notifications-tab-bar-item"
|
||||
id={NotificationTab.LOG}
|
||||
>
|
||||
Notification Log
|
||||
</TabBar.Item>
|
||||
<TabBar.Item
|
||||
className="nx-settings-tab-bar-item"
|
||||
className="nx-notifications-tab-bar-item"
|
||||
id={NotificationTab.SETTINGS}
|
||||
>
|
||||
Settings
|
||||
|
||||
@@ -46,7 +46,7 @@ export function FullPatchInput({ setFind, setParsedFind, setMatch, setReplacemen
|
||||
}
|
||||
|
||||
if (!replacement.match) throw new Error("No 'replacement.match' field");
|
||||
if (!replacement.replace) throw new Error("No 'replacement.replace' field");
|
||||
if (replacement.replace == null) throw new Error("No 'replacement.replace' field");
|
||||
|
||||
setFind(find instanceof RegExp ? `/${find.source}/` : find);
|
||||
setParsedFind(find);
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { FormSwitch } from "@components/FormSwitch";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { Forms, Parser, Switch, TextInput, useEffect, useState } from "@webpack/common";
|
||||
import { Forms, Parser, TextInput, useEffect, useState } from "@webpack/common";
|
||||
|
||||
const RegexGuide = {
|
||||
"\\i": "Special regex escape sequence that matches identifiers (varnames, classnames, etc.)",
|
||||
@@ -69,15 +70,14 @@ export function ReplacementInput({ replacement, setReplacement, replacementError
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Switch
|
||||
<FormSwitch
|
||||
className={Margins.top16}
|
||||
value={isFunc}
|
||||
onChange={setIsFunc}
|
||||
note="'replacement' will be evaled if this is toggled"
|
||||
title={"Treat as Function"}
|
||||
description="'replacement' will be evaled if this is toggled"
|
||||
hideBorder
|
||||
>
|
||||
Treat as Function
|
||||
</Switch>
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
.nx-author-modal-root {
|
||||
.vc-author-modal-root {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.nx-author-modal-header {
|
||||
.vc-author-modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.nx-author-modal-name {
|
||||
.vc-author-modal-name {
|
||||
text-transform: none;
|
||||
flex-grow: 0;
|
||||
background: var(--background-base-lowest);
|
||||
@@ -20,7 +20,7 @@
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
.nx-author-modal-name::before {
|
||||
.vc-author-modal-name::before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
@@ -34,19 +34,19 @@
|
||||
border-bottom-left-radius: 9999px;
|
||||
}
|
||||
|
||||
.nx-author-modal-avatar {
|
||||
.vc-author-modal-avatar {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.nx-author-modal-links {
|
||||
.vc-author-modal-links {
|
||||
margin-left: auto;
|
||||
gap: 4px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.nx-author-modal-plugins {
|
||||
.vc-author-modal-plugins {
|
||||
display: grid;
|
||||
gap: 0.5em;
|
||||
margin-top: 0.75em;
|
||||
|
||||
@@ -22,7 +22,7 @@ import Plugins from "~plugins";
|
||||
import { GithubButton, WebsiteButton } from "./LinkIconButton";
|
||||
import { PluginCard } from "./PluginCard";
|
||||
|
||||
const cl = classNameFactory("nx-author-modal-");
|
||||
const cl = classNameFactory("vc-author-modal-");
|
||||
|
||||
export function openContributorModal(user: User) {
|
||||
openModal(modalProps =>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.nx-settings-modal-link-icon {
|
||||
.vc-settings-modal-link-icon {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
border-radius: 50%;
|
||||
@@ -6,7 +6,7 @@
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
.nx-settings-modal-links {
|
||||
.vc-settings-modal-links {
|
||||
display: flex;
|
||||
gap: 0.2em;
|
||||
}
|
||||
@@ -12,12 +12,12 @@ import { MaskedLink, Tooltip } from "@webpack/common";
|
||||
|
||||
export function GithubLinkIcon() {
|
||||
const theme = getTheme() === Theme.Light ? "#000000" : "#FFFFFF";
|
||||
return <GithubIcon aria-hidden fill={theme} className={"nx-settings-modal-link-icon"} />;
|
||||
return <GithubIcon aria-hidden fill={theme} className={"vc-settings-modal-link-icon"} />;
|
||||
}
|
||||
|
||||
export function WebsiteLinkIcon() {
|
||||
const theme = getTheme() === Theme.Light ? "#000000" : "#FFFFFF";
|
||||
return <WebsiteIcon aria-hidden fill={theme} className={"nx-settings-modal-link-icon"} />;
|
||||
return <WebsiteIcon aria-hidden fill={theme} className={"vc-settings-modal-link-icon"} />;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
.nx-plugin-modal-title {
|
||||
.vc-plugin-modal-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nx-plugin-modal-title div {
|
||||
.vc-plugin-modal-title div {
|
||||
display: inline-block;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.nx-plugin-modal-info {
|
||||
.vc-plugin-modal-info {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nx-plugin-modal-description {
|
||||
.vc-plugin-modal-description {
|
||||
flex-grow: 1;
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import { useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { NxCard } from "@components/NxCard";
|
||||
import { NxCard, NxText } from "@components/NxComponents";
|
||||
import { debounce } from "@shared/debounce";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Margins } from "@utils/margins";
|
||||
@@ -40,7 +40,7 @@ import { PluginMeta } from "~plugins";
|
||||
import { OptionComponentMap } from "./components";
|
||||
import { openContributorModal } from "./ContributorModal";
|
||||
|
||||
const cl = classNameFactory("nx-plugin-modal-");
|
||||
const cl = classNameFactory("vc-plugin-modal-");
|
||||
|
||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
|
||||
@@ -88,7 +88,9 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
||||
|
||||
function renderSettings() {
|
||||
if (!hasSettings || !plugin.options)
|
||||
return <NxCard className={classes("nx-card-help", Margins.top16)}>There are no settings for this plugin.</NxCard>;
|
||||
return <NxCard variant="help" className={Margins.top16}>
|
||||
<NxText>There are no settings for this plugin.</NxText>
|
||||
</NxCard>;
|
||||
|
||||
const options = Object.entries(plugin.options).map(([key, setting]) => {
|
||||
if (setting.type === OptionType.CUSTOM || setting.hidden) return null;
|
||||
@@ -104,20 +106,21 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
||||
|
||||
const Component = OptionComponentMap[setting.type];
|
||||
return (
|
||||
<ErrorBoundary noop key={key}>
|
||||
<Component
|
||||
id={key}
|
||||
key={key}
|
||||
option={setting}
|
||||
onChange={debounce(onChange)}
|
||||
pluginSettings={pluginSettings}
|
||||
definedSettings={plugin.settings}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<NxCard className={Margins.top16}>
|
||||
<div className="nx-plugins-settings">
|
||||
<NxCard variant="grand" className={classes(Margins.top16, Margins.bottom8)}>
|
||||
<div className="vc-plugins-settings">
|
||||
{options}
|
||||
</div>
|
||||
</NxCard>
|
||||
@@ -177,25 +180,25 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent className={Margins.bottom16}>
|
||||
<Forms.FormSection>
|
||||
<section>
|
||||
<Flex className={cl("info")}>
|
||||
<Forms.FormText className={cl("description")}>{plugin.description}</Forms.FormText>
|
||||
</Flex>
|
||||
</Forms.FormSection>
|
||||
</section>
|
||||
|
||||
{!!plugin.settingsAboutComponent && (
|
||||
<div className={Margins.top16}>
|
||||
<Forms.FormSection>
|
||||
<section>
|
||||
<ErrorBoundary message="An error occurred while rendering this plugin's custom Info Component">
|
||||
<plugin.settingsAboutComponent />
|
||||
</ErrorBoundary>
|
||||
</Forms.FormSection>
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Forms.FormSection>
|
||||
<section>
|
||||
{renderSettings()}
|
||||
</Forms.FormSection>
|
||||
</section>
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Switch } from "@components/settings/Switch";
|
||||
import { Switch } from "@components/Switch";
|
||||
import { PluginOptionBoolean } from "@utils/types";
|
||||
import { React, useState } from "@webpack/common";
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { DefinedSettings, PluginOptionBase } from "@utils/types";
|
||||
import { Text } from "@webpack/common";
|
||||
import { PropsWithChildren } from "react";
|
||||
|
||||
export const cl = classNameFactory("nx-plugins-setting-");
|
||||
export const cl = classNameFactory("vc-plugins-setting-");
|
||||
|
||||
interface SettingBaseProps<T> {
|
||||
option: T;
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
.nx-plugins-setting-section {
|
||||
.vc-plugins-setting-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.nx-plugins-setting-content {
|
||||
.vc-plugins-setting-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.nx-plugins-setting-inline {
|
||||
.vc-plugins-setting-inline {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.nx-plugins-setting-label {
|
||||
.vc-plugins-setting-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25em;
|
||||
}
|
||||
|
||||
.nx-plugins-setting-title {
|
||||
.vc-plugins-setting-title {
|
||||
color: var(--header-primary);
|
||||
}
|
||||
|
||||
.nx-plugins-setting-description {
|
||||
.vc-plugins-setting-description {
|
||||
color: var(--header-secondary);
|
||||
}
|
||||
|
||||
.nx-plugins-setting-error {
|
||||
.vc-plugins-setting-error {
|
||||
color: var(--text-danger);
|
||||
}
|
||||
@@ -21,8 +21,9 @@ import "./styles.css";
|
||||
import * as DataStore from "@api/DataStore";
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { NxCard, NxCardTitle } from "@components/NxCard";
|
||||
import { NxCard, NxText, NxTitle } from "@components/NxComponents";
|
||||
import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||
import { ChangeList } from "@utils/ChangeList";
|
||||
import { Logger } from "@utils/Logger";
|
||||
@@ -37,7 +38,7 @@ import Plugins, { ExcludedPlugins } from "~plugins";
|
||||
|
||||
import { PluginCard } from "./PluginCard";
|
||||
|
||||
export const cl = classNameFactory("nx-plugins-");
|
||||
export const cl = classNameFactory("vc-plugins-");
|
||||
export const logger = new Logger("PluginSettings", "#a6d189");
|
||||
|
||||
const InputStyles = findByPropsLazy("inputWrapper", "inputError", "error");
|
||||
@@ -46,16 +47,18 @@ function ReloadRequiredCard({ required }: { required: boolean; }) {
|
||||
return (
|
||||
<>
|
||||
<NxCard className={cl("info-card")}>
|
||||
<NxCardTitle>Plugin Management</NxCardTitle>
|
||||
<span>Press the cog wheel or info icon to get more info on a plugin</span>
|
||||
<span> — Plugins with a cog wheel have settings you can modify!</span>
|
||||
<NxTitle>Plugin Management</NxTitle>
|
||||
<NxText>
|
||||
Press the cog wheel or info icon to get more info on a plugin —
|
||||
Plugins with a cog wheel have settings you can modify!
|
||||
</NxText>
|
||||
</NxCard>
|
||||
{required ? (
|
||||
<NxCard className={classes("nx-card-warning", Margins.bottom16)}>
|
||||
<NxCard variant="warning" className={Margins.bottom16}>
|
||||
<Flex flexDirection="row" style={{ justifyContent: "space-between" }}>
|
||||
<div>
|
||||
<NxCardTitle>Restart required!</NxCardTitle>
|
||||
<span>Restart now to apply new plugins and their settings</span>
|
||||
<NxTitle>Restart required!</NxTitle>
|
||||
<NxText>Restart now to apply new plugins and their settings</NxText>
|
||||
</div>
|
||||
<Button onClick={() => location.reload()} className={cl("restart-button")}>
|
||||
Restart
|
||||
@@ -258,8 +261,11 @@ function PluginSettings() {
|
||||
</Forms.FormTitle> */}
|
||||
|
||||
<div className={classes(Margins.bottom20, cl("filter-controls"))}>
|
||||
<ErrorBoundary noop>
|
||||
<TextInput autoFocus value={searchValue.value} placeholder="Search for a plugin..." onChange={onSearch} />
|
||||
</ErrorBoundary>
|
||||
<div className={InputStyles.inputWrapper}>
|
||||
<ErrorBoundary noop>
|
||||
<Select
|
||||
options={[
|
||||
{ label: "Show All", value: SearchStatus.ALL, default: true },
|
||||
@@ -274,6 +280,7 @@ function PluginSettings() {
|
||||
isSelected={v => v === searchValue.status}
|
||||
closeOnSelect={true}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.nx-plugins-grid {
|
||||
.vc-plugins-grid {
|
||||
margin-top: 16px;
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
}
|
||||
|
||||
.nx-plugins-info-button {
|
||||
.vc-plugins-info-button {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
padding: 0;
|
||||
@@ -31,18 +31,18 @@
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.nx-plugins-settings-button:hover {
|
||||
.vc-plugins-settings-button:hover {
|
||||
color: var(--interactive-hover);
|
||||
}
|
||||
|
||||
.nx-plugins-filter-controls {
|
||||
.vc-plugins-filter-controls {
|
||||
display: grid;
|
||||
height: 40px;
|
||||
gap: 10px;
|
||||
grid-template-columns: 1fr 200px;
|
||||
}
|
||||
|
||||
.nx-addon-badge {
|
||||
.vc-addon-badge {
|
||||
padding: 0 6px;
|
||||
font-family: var(--font-display);
|
||||
font-weight: 500;
|
||||
@@ -54,23 +54,23 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nx-plugins-dep-name {
|
||||
.vc-plugins-dep-name {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.nx-plugins-info-card {
|
||||
.vc-plugins-info-card {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.nx-plugins-restart-button {
|
||||
.vc-plugins-restart-button {
|
||||
background-color: var(--yellow-300) !important;
|
||||
}
|
||||
|
||||
.nx-plugins-info-icon:not(:hover, :focus) {
|
||||
.vc-plugins-info-icon:not(:hover, :focus) {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.nx-plugins-noResults {
|
||||
.vc-plugins-noResults {
|
||||
margin-top: 16px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
@@ -78,7 +78,7 @@
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.nx-plugins-settings {
|
||||
.vc-plugins-settings {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25em;
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
.nx-settings-tab-bar {
|
||||
.vc-settings-tab-bar {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.nx-settings-tab-bar-item {
|
||||
.vc-settings-tab-bar-item {
|
||||
margin-right: 32px;
|
||||
padding-bottom: 16px;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.nx-settings-card {
|
||||
.vc-settings-card {
|
||||
padding: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.nx-backup-restore-card {
|
||||
.vc-backup-restore-card {
|
||||
background-color: var(--info-warning-background);
|
||||
border-color: var(--info-warning-foreground);
|
||||
color: var(--info-warning-text);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.nx-settings-theme-links {
|
||||
.vc-settings-theme-links {
|
||||
/* Needed to fix bad themes that hide certain textarea elements for whatever eldritch reason */
|
||||
display: inline-block !important;
|
||||
color: var(--text-default) !important;
|
||||
@@ -38,27 +38,27 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nx-settings-theme-links::placeholder {
|
||||
.vc-settings-theme-links::placeholder {
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
.nx-settings-theme-links:focus {
|
||||
.vc-settings-theme-links:focus {
|
||||
background-color: var(--background-tertiary);
|
||||
}
|
||||
|
||||
.nx-cloud-settings-sync-grid {
|
||||
.vc-cloud-settings-sync-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-gap: 1em;
|
||||
}
|
||||
|
||||
.nx-cloud-erase-data-danger-btn {
|
||||
.vc-cloud-erase-data-danger-btn {
|
||||
color: var(--white-500);
|
||||
background-color: var(--button-danger-background);
|
||||
}
|
||||
|
||||
.nx-text-selectable,
|
||||
.nx-text-selectable :where([class*="text" i], [class*="title" i]) {
|
||||
.vc-text-selectable,
|
||||
.vc-text-selectable :where([class*="text" i], [class*="title" i]) {
|
||||
/* make text selectable, silly discord makes the entirety of settings not selectable */
|
||||
user-select: text;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import "@components/settings/tabs/styles.css";
|
||||
|
||||
import { Flex } from "@components/Flex";
|
||||
import { NxCard } from "@components/NxCard";
|
||||
import { NxCard, NxText } from "@components/NxComponents";
|
||||
import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { downloadSettingsBackup, uploadSettingsBackup } from "@utils/settingsSync";
|
||||
@@ -28,8 +28,8 @@ import { Button, Text } from "@webpack/common";
|
||||
export function BackupAndRestoreTab() {
|
||||
return (
|
||||
<SettingsTab title="Backup & Restore">
|
||||
<NxCard className={`nx-card-warning ${Margins.bottom16}`}>
|
||||
<span>Importing a settings file will overwrite your current settings.</span>
|
||||
<NxCard variant="warning" className={Margins.bottom16}>
|
||||
<NxText>Importing a settings file will overwrite your current settings.</NxText>
|
||||
</NxCard>
|
||||
<Text variant="text-md/normal" className={Margins.bottom8}>
|
||||
You can import and export your Nexulien settings as a JSON file.
|
||||
|
||||
@@ -19,14 +19,15 @@
|
||||
import { showNotification } from "@api/Notifications";
|
||||
import { Settings, useSettings } from "@api/Settings";
|
||||
import { CheckedTextInput } from "@components/CheckedTextInput";
|
||||
import { FormSwitch } from "@components/FormSwitch";
|
||||
import { Grid } from "@components/Grid";
|
||||
import { Link } from "@components/Link";
|
||||
import { NxCard, NxCardTitle } from "@components/NxCard";
|
||||
import { NxCard, NxText, NxTitle } from "@components/NxComponents";
|
||||
import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||
import { authorizeCloud, checkCloudUrlCsp, cloudLogger, deauthorizeCloud, getCloudAuth, getCloudUrl } from "@utils/cloud";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { deleteCloudSettings, getCloudSettings, putCloudSettings } from "@utils/settingsSync";
|
||||
import { Alerts, Button, Forms, Switch, Tooltip } from "@webpack/common";
|
||||
import { Alerts, Button, Forms, Tooltip } from "@webpack/common";
|
||||
|
||||
function validateUrl(url: string) {
|
||||
try {
|
||||
@@ -70,22 +71,23 @@ function SettingsSyncSection() {
|
||||
const sectionEnabled = cloud.authenticated && cloud.settingsSync;
|
||||
|
||||
return (
|
||||
<NxCard className={Margins.top16}>
|
||||
<NxCardTitle>Settings Sync</NxCardTitle>
|
||||
<section className={Margins.top16}>
|
||||
<NxCard>
|
||||
<NxTitle>Settings Sync</NxTitle>
|
||||
|
||||
<Forms.FormText variant="text-md/normal" className={Margins.bottom20}>
|
||||
Synchronize your settings to the cloud. This allows easy synchronization across multiple devices with
|
||||
minimal effort.
|
||||
</Forms.FormText>
|
||||
<Switch
|
||||
<FormSwitch
|
||||
key="cloud-sync"
|
||||
disabled={!cloud.authenticated}
|
||||
title="Settings Sync"
|
||||
value={cloud.settingsSync}
|
||||
onChange={v => { cloud.settingsSync = v; }}
|
||||
className="nx-removeSwitchDivider"
|
||||
>
|
||||
Settings Sync
|
||||
</Switch>
|
||||
<div className="nx-cloud-settings-sync-grid">
|
||||
disabled={!cloud.authenticated}
|
||||
/>
|
||||
<div className="vc-cloud-settings-sync-grid">
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
disabled={!sectionEnabled}
|
||||
@@ -117,6 +119,7 @@ function SettingsSyncSection() {
|
||||
</Button>
|
||||
</div>
|
||||
</NxCard>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -125,15 +128,20 @@ function CloudTab() {
|
||||
|
||||
return (
|
||||
<SettingsTab title="Vencord Cloud">
|
||||
<NxCard className={Margins.bottom16}>
|
||||
<section className={Margins.top16}>
|
||||
<NxCard className={Margins.bottom20}>
|
||||
<NxText>
|
||||
Vencord comes with a cloud integration that adds goodies like settings sync across devices.
|
||||
It <Link href="https://vencord.dev/cloud/privacy">respects your privacy</Link>, and
|
||||
the <Link href="https://github.com/Vencord/Backend">source code</Link> is AGPL 3.0 licensed so you
|
||||
can host it yourself. It may or may not work with Nexulien; use with caution.
|
||||
</NxText>
|
||||
</NxCard>
|
||||
|
||||
<Switch
|
||||
<FormSwitch
|
||||
key="backend"
|
||||
title="Enable Cloud Integrations"
|
||||
description="This will request authorization if you have not yet set up cloud integrations."
|
||||
value={settings.cloud.authenticated}
|
||||
onChange={v => {
|
||||
if (v)
|
||||
@@ -141,15 +149,12 @@ function CloudTab() {
|
||||
else
|
||||
settings.cloud.authenticated = v;
|
||||
}}
|
||||
note="This will request authorization if you have not yet set up cloud integrations."
|
||||
className={!settings.cloud.authenticated ? "nx-removeSwitchDivider" : ""}
|
||||
>
|
||||
Enable Cloud Integrations
|
||||
</Switch>
|
||||
hideBorder={!settings.cloud.authenticated}
|
||||
/>
|
||||
|
||||
{settings.cloud.authenticated ? <>
|
||||
<NxCard>
|
||||
<NxCardTitle>Backend URL</NxCardTitle>
|
||||
<NxTitle>Backend URL</NxTitle>
|
||||
<Forms.FormText className={Margins.bottom8}>
|
||||
Which backend to use when using cloud integrations.
|
||||
</Forms.FormText>
|
||||
@@ -185,16 +190,17 @@ function CloudTab() {
|
||||
body: "Once your data is erased, we cannot recover it. There's no going back!",
|
||||
onConfirm: eraseAllData,
|
||||
confirmText: "Erase it!",
|
||||
confirmColor: "nx-cloud-erase-data-danger-btn",
|
||||
confirmColor: "vc-cloud-erase-data-danger-btn",
|
||||
cancelText: "Nevermind"
|
||||
})}
|
||||
>
|
||||
Erase All Data
|
||||
</Button>
|
||||
</Grid>
|
||||
</NxCard>
|
||||
</NxCard >
|
||||
<SettingsSyncSection />
|
||||
</> : <></>}
|
||||
</section>
|
||||
</SettingsTab>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,12 +8,11 @@ import { Settings, useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons";
|
||||
import { Link } from "@components/Link";
|
||||
import { NxCard, NxCardTitle } from "@components/NxCard";
|
||||
import { NxCard, NxText, NxTitle } from "@components/NxComponents";
|
||||
import { QuickAction, QuickActionContainer } from "@components/settings/QuickAction";
|
||||
import { openPluginModal } from "@components/settings/tabs/plugins/PluginModal";
|
||||
import { UserThemeHeader } from "@main/themes";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { findLazy } from "@webpack";
|
||||
import { Forms, useEffect, useRef, useState } from "@webpack/common";
|
||||
import ClientThemePlugin from "plugins/clientTheme";
|
||||
@@ -21,7 +20,7 @@ import type { ComponentType, Ref, SyntheticEvent } from "react";
|
||||
|
||||
import { ThemeCard } from "./ThemeCard";
|
||||
|
||||
const cl = classNameFactory("nx-settings-theme-");
|
||||
const cl = classNameFactory("vc-settings-theme-");
|
||||
|
||||
type FileInput = ComponentType<{
|
||||
ref: Ref<HTMLInputElement>;
|
||||
@@ -86,7 +85,7 @@ export function LocalThemesTab() {
|
||||
return (
|
||||
<>
|
||||
<NxCard className={cl("info-card")}>
|
||||
<NxCardTitle>Find Themes:</NxCardTitle>
|
||||
<NxTitle>Find Themes:</NxTitle>
|
||||
<div style={{ marginBottom: ".5em", display: "flex", flexDirection: "column" }}>
|
||||
<ul>
|
||||
<li>
|
||||
@@ -101,16 +100,20 @@ export function LocalThemesTab() {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<span>If using the BD site, click on "Download" and place the downloaded .theme.css file into your themes folder.</span>
|
||||
<NxText>If using the BD site, click on "Download" and place the downloaded <code className="nx-code-new">.theme.css</code> file into your themes folder.</NxText>
|
||||
</NxCard>
|
||||
|
||||
<NxCard className={classes("nx-card-help", Margins.bottom16)}>
|
||||
<NxCardTitle>External Resources</NxCardTitle>
|
||||
<span>For security reasons, loading resources (styles, fonts, images, ...) from most sites is blocked.</span>
|
||||
<span> Make sure all your assets are hosted on GitHub, GitLab, Codeberg, Imgur, Discord or Google Fonts.</span>
|
||||
<NxCard variant="help" className={Margins.bottom16}>
|
||||
<NxTitle>External Resources</NxTitle>
|
||||
<NxText>
|
||||
For security reasons, loading resources (styles, fonts, images, ...) from most sites is blocked.
|
||||
Make sure all your assets are hosted on GitHub, GitLab, Codeberg, Imgur, Discord or Google Fonts.
|
||||
</NxText>
|
||||
</NxCard>
|
||||
|
||||
<Forms.FormSection title="Local Themes">
|
||||
|
||||
<section>
|
||||
<Forms.FormTitle tag="h5">Local Themes</Forms.FormTitle>
|
||||
<QuickActionContainer title="Manage Local Themes">
|
||||
<>
|
||||
{IS_WEB ?
|
||||
@@ -175,7 +178,7 @@ export function LocalThemesTab() {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Forms.FormSection>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
*/
|
||||
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { NxCard, NxCardTitle } from "@components/NxCard";
|
||||
import { NxCard, NxText, NxTitle } from "@components/NxComponents";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { Forms, TextArea, useState } from "@webpack/common";
|
||||
|
||||
export function OnlineThemesTab() {
|
||||
@@ -28,30 +27,33 @@ export function OnlineThemesTab() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<NxCard className={`${classes("nx-card-warning", Margins.bottom16)}`}>
|
||||
<span>
|
||||
<NxCard variant="warning" className={Margins.bottom16}>
|
||||
<NxText>
|
||||
This section is for advanced users. If you are having difficulties using it, use the
|
||||
Local Themes tab instead.
|
||||
</span>
|
||||
</NxText>
|
||||
</NxCard>
|
||||
<NxCard className="nx-settings-card">
|
||||
<NxCardTitle tag="h5">Paste links to css files here</NxCardTitle>
|
||||
<span>One link per line</span><br></br>
|
||||
<span>You can prefix lines with @light or @dark to toggle them based on your Discord theme</span><br></br>
|
||||
<span>Make sure to use direct links to files (raw or github.io)!</span>
|
||||
<NxCard className="vc-settings-card">
|
||||
<NxTitle>Paste links to css files here</NxTitle>
|
||||
<NxText>
|
||||
<span>— One link per line</span><br></br>
|
||||
<span>— You can prefix lines with <code className="nx-code-new">@light</code> or <code className="nx-code-new">@dark</code> to toggle them based on your Discord theme</span><br></br>
|
||||
<span>— Make sure to use direct links to files (raw or github.io)!</span>
|
||||
</NxText>
|
||||
</NxCard>
|
||||
|
||||
<Forms.FormSection title="Online Themes" tag="h5">
|
||||
<section>
|
||||
<Forms.FormTitle tag="h5">Online Themes</Forms.FormTitle>
|
||||
<TextArea
|
||||
value={themeText}
|
||||
onChange={setThemeText}
|
||||
className={"nx-settings-theme-links"}
|
||||
className={"vc-settings-theme-links"}
|
||||
placeholder="Enter Theme Links..."
|
||||
spellCheck={false}
|
||||
onBlur={onBlur}
|
||||
rows={10}
|
||||
/>
|
||||
</Forms.FormSection>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import "./styles.css";
|
||||
|
||||
import { Link } from "@components/Link";
|
||||
import { NxCard, NxCardTitle } from "@components/NxCard";
|
||||
import { NxCard, NxTitle } from "@components/NxComponents";
|
||||
import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||
import { getStylusWebStoreUrl } from "@utils/web";
|
||||
import { React, TabBar, useState } from "@webpack/common";
|
||||
@@ -41,18 +41,18 @@ function ThemesTab() {
|
||||
<TabBar
|
||||
type="top"
|
||||
look="brand"
|
||||
className="nx-settings-tab-bar"
|
||||
className="vc-settings-tab-bar"
|
||||
selectedItem={currentTab}
|
||||
onItemSelect={setCurrentTab}
|
||||
>
|
||||
<TabBar.Item
|
||||
className="nx-settings-tab-bar-item"
|
||||
className="vc-settings-tab-bar-item"
|
||||
id={ThemeTab.LOCAL}
|
||||
>
|
||||
Local Themes
|
||||
</TabBar.Item>
|
||||
<TabBar.Item
|
||||
className="nx-settings-tab-bar-item"
|
||||
className="vc-settings-tab-bar-item"
|
||||
id={ThemeTab.ONLINE}
|
||||
>
|
||||
Online Themes
|
||||
@@ -71,7 +71,7 @@ function UserscriptThemesTab() {
|
||||
return (
|
||||
<SettingsTab title="Themes">
|
||||
<NxCard>
|
||||
<NxCardTitle>Themes are not supported on the Userscript!</NxCardTitle>
|
||||
<NxTitle>Themes are not supported on the Userscript!</NxTitle>
|
||||
<span>
|
||||
You can instead install themes with the <Link href={getStylusWebStoreUrl()}>Stylus extension</Link>!
|
||||
</span>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
.nx-settings-theme-grid {
|
||||
.vc-settings-theme-grid {
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
}
|
||||
|
||||
.nx-settings-theme-card {
|
||||
.vc-settings-theme-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--background-base-lower-alt);
|
||||
@@ -16,12 +16,12 @@
|
||||
transition-property: box-shadow, transform, background, opacity;
|
||||
}
|
||||
|
||||
.nx-settings-theme-info-card {
|
||||
.vc-settings-theme-info-card {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.nx-settings-theme-card-text {
|
||||
.vc-settings-theme-card-text {
|
||||
text-overflow: ellipsis;
|
||||
height: 1.2em;
|
||||
margin-bottom: 2px;
|
||||
@@ -29,21 +29,21 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nx-settings-theme-author::before {
|
||||
.vc-settings-theme-author::before {
|
||||
content: "by ";
|
||||
}
|
||||
|
||||
.nx-settings-theme-validator-card {
|
||||
.vc-settings-theme-validator-card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.nx-settings-csp-list {
|
||||
.vc-settings-csp-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nx-settings-csp-row {
|
||||
.vc-settings-csp-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { ErrorCard } from "@components/ErrorCard";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Link } from "@components/Link";
|
||||
import { NxCard, NxCardTitle } from "@components/NxCard";
|
||||
import { NxCard, NxTitle } from "@components/NxComponents";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { relaunch } from "@utils/native";
|
||||
@@ -34,7 +34,7 @@ export function HashLink({ repo, hash, disabled = false }: { repo: string, hash:
|
||||
|
||||
export function Changes({ updates, repo, repoPending }: CommonProps & { updates: typeof changes; }) {
|
||||
return (
|
||||
<NxCard style={{ padding: "0 0.5em" }} className="nx-updater-changes">
|
||||
<NxCard style={{ padding: "0 0.5em" }} className="vc-updater-changes">
|
||||
{updates.map(({ hash, author, message }) => (
|
||||
<div
|
||||
key={hash}
|
||||
@@ -81,7 +81,7 @@ export function Updatable(props: CommonProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<NxCard className={classes(isOutdated ? "nx-card-warning" : (!updates && updateError ? "nx-card-danger" : "nx-card-positive"), Margins.bottom16)}>
|
||||
<NxCard variant={isOutdated ? "warning" : (!updates && updateError ? "danger" : "positive")} className={Margins.bottom16}>
|
||||
{!updates && updateError ? (
|
||||
<>
|
||||
<Forms.FormText>Failed to check for updates. Check the console for more info</Forms.FormText>
|
||||
@@ -154,9 +154,9 @@ export function Repository({ repo, repoPending, err }: CommonProps) {
|
||||
return (
|
||||
<>
|
||||
<NxCard>
|
||||
<NxCardTitle>Repository</NxCardTitle>
|
||||
<NxTitle>Repository</NxTitle>
|
||||
|
||||
<Forms.FormText className="nx-text-selectable">
|
||||
<Forms.FormText className="vc-text-selectable">
|
||||
{repoPending
|
||||
? repo
|
||||
: err
|
||||
@@ -167,7 +167,7 @@ export function Repository({ repo, repoPending, err }: CommonProps) {
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
{" "}(<code className="nx-updater-repo-hash"><HashLink hash={gitHash} repo={repo} disabled={repoPending} /></code>)
|
||||
{" "}(<code className="nx-code"><HashLink hash={gitHash} repo={repo} disabled={repoPending} /></code>)
|
||||
</Forms.FormText>
|
||||
</NxCard>
|
||||
</>
|
||||
|
||||
@@ -19,11 +19,12 @@
|
||||
import "./styles.css";
|
||||
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { FormSwitch } from "@components/FormSwitch";
|
||||
import { handleSettingsTabError, SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||
import { ModalCloseButton, ModalContent, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { getRepo, isNewer, UpdateLogger } from "@utils/updater";
|
||||
import { Forms, React, Switch } from "@webpack/common";
|
||||
import { Forms, React } from "@webpack/common";
|
||||
|
||||
import { CommonProps, Newer, Repository, Updatable } from "./Components";
|
||||
|
||||
@@ -45,21 +46,19 @@ function Updater() {
|
||||
<SettingsTab title="Nexulien Updater">
|
||||
<Forms.FormTitle tag="h5">Updater Settings</Forms.FormTitle>
|
||||
|
||||
<Switch
|
||||
<FormSwitch
|
||||
title="Automatically update"
|
||||
description="Automatically update Vencord without confirmation prompt"
|
||||
value={settings.autoUpdate}
|
||||
onChange={(v: boolean) => settings.autoUpdate = v}
|
||||
note="Automatically update Vencord without confirmation prompt"
|
||||
>
|
||||
Automatically update
|
||||
</Switch>
|
||||
<Switch
|
||||
/>
|
||||
<FormSwitch
|
||||
title="Get notified when an automatic update completes"
|
||||
description="Show a notification when Vencord automatically updates"
|
||||
value={settings.autoUpdateNotification}
|
||||
onChange={(v: boolean) => settings.autoUpdateNotification = v}
|
||||
note="Show a notification when Vencord automatically updates"
|
||||
disabled={!settings.autoUpdate}
|
||||
>
|
||||
Get notified when an automatic update completes
|
||||
</Switch>
|
||||
/>
|
||||
|
||||
<Forms.FormTitle tag="h5">Updates</Forms.FormTitle>
|
||||
|
||||
@@ -85,8 +84,8 @@ export const openUpdaterModal = IS_UPDATER_DISABLED
|
||||
try {
|
||||
openModal(wrapTab((modalProps: ModalProps) => (
|
||||
<ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<ModalContent className="nx-updater-modal">
|
||||
<ModalCloseButton onClick={modalProps.onClose} className="nx-updater-modal-close-button" />
|
||||
<ModalContent className="vc-updater-modal">
|
||||
<ModalCloseButton onClick={modalProps.onClose} className="vc-updater-modal-close-button" />
|
||||
<UpdaterTab />
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.nx-updater-changes {
|
||||
.vc-updater-changes {
|
||||
background: var(--background-code);
|
||||
border: 1px solid var(--border-normal);
|
||||
margin-top: 16px !important;
|
||||
@@ -6,21 +6,10 @@
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.nx-updater-modal {
|
||||
.vc-updater-modal {
|
||||
padding: 1.5em !important;
|
||||
}
|
||||
|
||||
.nx-updater-modal-close-button {
|
||||
.vc-updater-modal-close-button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.nx-updater-repo-hash {
|
||||
border-radius: 4px;
|
||||
font-size: 85%;
|
||||
height: auto;
|
||||
margin: -.2em 0;
|
||||
padding: 0 .2em;
|
||||
text-indent: 0;
|
||||
white-space: pre-wrap;
|
||||
background: var(--background-base-lowest);
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import { useSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { identity } from "@utils/misc";
|
||||
import { Forms, Select } from "@webpack/common";
|
||||
@@ -15,6 +16,7 @@ export function VibrancySettings() {
|
||||
return (
|
||||
<>
|
||||
<Forms.FormTitle tag="h5">Window vibrancy style (requires restart)</Forms.FormTitle>
|
||||
<ErrorBoundary noop>
|
||||
<Select
|
||||
className={Margins.bottom20}
|
||||
placeholder="Window vibrancy style"
|
||||
@@ -74,7 +76,9 @@ export function VibrancySettings() {
|
||||
]}
|
||||
select={v => settings.macosVibrancyStyle = v}
|
||||
isSelected={v => settings.macosVibrancyStyle === v}
|
||||
serialize={identity} />
|
||||
serialize={identity}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ import { Button, Forms, Select, Slider, Text } from "@webpack/common";
|
||||
|
||||
export function NotificationSection() {
|
||||
return (
|
||||
<Forms.FormSection className={Margins.top16} title="Vencord Notifications" tag="h5">
|
||||
<section className={Margins.top16}>
|
||||
<Forms.FormTitle tag="h5">Vencord Notifications</Forms.FormTitle>
|
||||
<Flex>
|
||||
<Button onClick={openNotificationSettingsModal}>
|
||||
Notification Settings
|
||||
@@ -24,7 +25,7 @@ export function NotificationSection() {
|
||||
View Notification Log
|
||||
</Button>
|
||||
</Flex>
|
||||
</Forms.FormSection>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,9 @@ import "@components/settings/styles.css";
|
||||
|
||||
import { Settings, useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { FormSwitch } from "@components/FormSwitch";
|
||||
import { FolderIcon, GithubIcon, PaintbrushIcon, RestartIcon } from "@components/index";
|
||||
import { NxCard } from "@components/NxCard";
|
||||
import { NxCard, NxText } from "@components/NxComponents";
|
||||
import { NxMascot } from "@components/settings/Mascot";
|
||||
import { QuickAction, QuickActionContainer } from "@components/settings/QuickAction";
|
||||
import { SpecialCard } from "@components/settings/SpecialCard";
|
||||
@@ -33,14 +34,14 @@ import { gitRemote } from "@shared/vencordUserAgent";
|
||||
import { IS_MAC, IS_WINDOWS } from "@utils/constants";
|
||||
import { openInviteModal } from "@utils/discord";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { isPluginDev } from "@utils/misc";
|
||||
import { classes, isPluginDev } from "@utils/misc";
|
||||
import { closeAllModals } from "@utils/modal";
|
||||
import { relaunch } from "@utils/native";
|
||||
import { Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, React, Switch, UserStore } from "@webpack/common";
|
||||
import { Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, React, UserStore } from "@webpack/common";
|
||||
|
||||
import { VibrancySettings } from "./MacVibrancySettings";
|
||||
|
||||
const cl = classNameFactory("nx-settings-");
|
||||
const cl = classNameFactory("vc-settings-");
|
||||
|
||||
const CONTRIB_IMAGE = "https://cdn.discordapp.com/emojis/1337858798664024156.png";
|
||||
const CONTRIB_BACKGROUND_IMAGE = "https://media.discordapp.net/stickers/1337878381517078649.png?size=2048";
|
||||
@@ -94,14 +95,13 @@ function Switches() {
|
||||
}>;
|
||||
|
||||
return Switches.map(s => s && (
|
||||
<Switch
|
||||
<FormSwitch
|
||||
key={s.key}
|
||||
title={s.title}
|
||||
description={s.note}
|
||||
value={settings[s.key]}
|
||||
onChange={v => settings[s.key] = v}
|
||||
note={s.note}
|
||||
>
|
||||
{s.title}
|
||||
</Switch>
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
@@ -116,15 +116,19 @@ function VencordSettings() {
|
||||
<>
|
||||
<SettingsTab title="Nexulien Settings">
|
||||
<HeaderCard />
|
||||
|
||||
{isPluginDev(user?.id) && !hideContributorCard && (
|
||||
<SpecialCard
|
||||
title="Thank you for contributing!"
|
||||
title="Contributions"
|
||||
subtitle="Thank you for contributing!"
|
||||
description="Since you've contributed to Nexulien, you now have a cool new badge!"
|
||||
cardImage={CONTRIB_IMAGE}
|
||||
backgroundImage={CONTRIB_BACKGROUND_IMAGE}
|
||||
backgroundGradient="linear-gradient(to left, var(--nx-green), var(--nx-purple))"
|
||||
/>
|
||||
)}
|
||||
|
||||
<section>
|
||||
<QuickActionContainer title="Quick Actions" columns="2">
|
||||
<QuickAction
|
||||
Icon={PaintbrushIcon}
|
||||
@@ -150,33 +154,31 @@ function VencordSettings() {
|
||||
action={() => VencordNative.native.openExternal("https://github.com/" + gitRemote)}
|
||||
/>
|
||||
</QuickActionContainer>
|
||||
</section>
|
||||
|
||||
<Forms.FormSection className={Margins.top16} title="Settings" tag="h5">
|
||||
<section className={Margins.top16}>
|
||||
<Forms.FormTitle tag="h5">Settings</Forms.FormTitle>
|
||||
{showHint ?
|
||||
<NxCard className={`nx-card-help ${Margins.bottom16}`}>
|
||||
<NxCard variant="help" size="small" className={Margins.bottom16}>
|
||||
<NxText size="small">
|
||||
If you'd like to change the position of the Nexulien section, change the header card size, or just hide this hint, you can do so in the
|
||||
{" "}<button
|
||||
style={{ all: undefined, color: "var(--text-link)", display: "inline-block", backgroundColor: "transparent", padding: 0, fontSize: 16 }}
|
||||
onClick={() => openPluginModal(Vencord.Plugins.plugins.Settings)}
|
||||
>
|
||||
{" "}<a onClick={() => openPluginModal(Vencord.Plugins.plugins.Settings)}>
|
||||
settings of the Settings plugin
|
||||
</button>!
|
||||
</a>!
|
||||
</NxText>
|
||||
</NxCard> : <></>}
|
||||
|
||||
<Switches />
|
||||
</Forms.FormSection>
|
||||
|
||||
|
||||
{needsVibrancySettings && <VibrancySettings />}
|
||||
</SettingsTab>
|
||||
</section>
|
||||
{BackupAndRestoreTab()}
|
||||
</SettingsTab>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function nexulien() {
|
||||
const audioElement = document.createElement("audio");
|
||||
const logo = document.getElementById("nx-settings-logo");
|
||||
const logo = document.getElementById("vc-settings-logo");
|
||||
|
||||
const audioArray = [
|
||||
"https://raw.githubusercontent.com/Nexulien/Assets/main/tts/bonzi.wav", // 🟣🐒
|
||||
@@ -190,7 +192,7 @@ function nexulien() {
|
||||
audioElement.volume = 0.5;
|
||||
audioElement.play();
|
||||
window.setTimeout(function () {
|
||||
logo!.style = "animation: nx-settings-logo-boioioing 0.4s cubic-bezier(0.215, 0.610, 0.355, 1);";
|
||||
logo!.style = "animation: vc-settings-logo-boioioing 0.4s cubic-bezier(0.215, 0.610, 0.355, 1);";
|
||||
}, 200);
|
||||
window.setTimeout(function () {
|
||||
logo!.removeAttribute("style");
|
||||
@@ -213,8 +215,8 @@ function HeaderCard() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{headerCardSize !== "none" ?
|
||||
<NxCard className={cl("card", "header", headerCardSize === "minimal" ? "header-minimal" : "")}>
|
||||
{headerCardSize !== "none" &&
|
||||
<NxCard className={classes(cl("card", "header", headerCardSize === "minimal" && "header-minimal"), "nx-card-grand")}>
|
||||
<div>
|
||||
<span className={cl("logo-container")} onClick={() => nexulien()}>
|
||||
<svg width="250" height="50" viewBox="0 0 250 50" fill="none" xmlns="http://www.w3.org/2000/svg" className={cl("logo")} id={cl("logo")}>
|
||||
@@ -229,13 +231,13 @@ function HeaderCard() {
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
{headerCardSize === "default" ? <>
|
||||
{headerCardSize === "default" ? <NxText>
|
||||
{/* ↓ Factual Information */}
|
||||
<span>...the best (worst) discord client mod.</span>
|
||||
<span>...the best (worst) discord client mod.</span><br /><br />
|
||||
<span>Nexulien doesn't need donations! Please go support <a href="https://github.com/sponsors/Vendicated" target="_blank" rel="noreferrer">Vendicated</a> instead!</span>
|
||||
</> : <></>}
|
||||
</NxText> : <></>}
|
||||
|
||||
<div className={cl("buttonRow", headerCardSize === "minimal" ? "buttonRow-minimal" : "")}>
|
||||
<div className={cl("buttonRow", headerCardSize === "minimal" && "buttonRow-minimal")}>
|
||||
<Button
|
||||
size={headerCardSize === "minimal" ? Button.Sizes.SMALL : Button.Sizes.MEDIUM}
|
||||
onClick={() => window.open("https://github.com/Nexulien")}
|
||||
@@ -258,9 +260,9 @@ function HeaderCard() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{headerCardSize === "default" ? <NxMascot /> : <></>}
|
||||
{headerCardSize === "default" && <NxMascot />}
|
||||
</NxCard>
|
||||
: <></>}
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,24 @@ import * as Webpack from "@webpack";
|
||||
import { wreq } from "@webpack";
|
||||
import { AnyModuleFactory } from "webpack";
|
||||
|
||||
function getWebpackChunkMap() {
|
||||
const sym = Symbol();
|
||||
let v: Record<PropertyKey, string> | null = null;
|
||||
|
||||
Object.defineProperty(Object.prototype, sym, {
|
||||
get() {
|
||||
v = this;
|
||||
return "";
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
|
||||
wreq.u(sym);
|
||||
delete Object.prototype[sym];
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
export async function loadLazyChunks() {
|
||||
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
|
||||
|
||||
@@ -153,17 +171,10 @@ export async function loadLazyChunks() {
|
||||
}
|
||||
|
||||
// All chunks Discord has mapped to asset files, even if they are not used anymore
|
||||
const allChunks = [] as PropertyKey[];
|
||||
|
||||
// Matches "id" or id:
|
||||
for (const currentMatch of String(wreq.u).matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) {
|
||||
const id = currentMatch[1] ?? currentMatch[2];
|
||||
if (id == null) continue;
|
||||
|
||||
const numId = Number(id);
|
||||
allChunks.push(Number.isNaN(numId) ? id : numId);
|
||||
}
|
||||
const chunkMap = getWebpackChunkMap();
|
||||
if (!chunkMap) throw new Error("Failed to get chunk map");
|
||||
|
||||
const allChunks = Object.keys(chunkMap).map(id => Number.isNaN(Number(id)) ? id : Number(id));
|
||||
if (allChunks.length === 0) throw new Error("Failed to get all chunks");
|
||||
|
||||
// Chunks which our regex could not catch to load
|
||||
|
||||
6
src/globals.d.ts
vendored
6
src/globals.d.ts
vendored
@@ -16,8 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { LoDashStatic } from "lodash";
|
||||
|
||||
declare global {
|
||||
/**
|
||||
* This exists only at build time, so references to it in patches should insert it
|
||||
@@ -65,9 +63,7 @@ declare global {
|
||||
export var Vesktop: any;
|
||||
export var VesktopNative: any;
|
||||
|
||||
interface Window extends Record<PropertyKey, any> {
|
||||
_: LoDashStatic;
|
||||
}
|
||||
interface Window extends Record<PropertyKey, any> { }
|
||||
}
|
||||
|
||||
export { };
|
||||
|
||||
@@ -113,7 +113,7 @@ const patchCsp = (headers: PolicyMap) => {
|
||||
pushDirective("script-src", "'unsafe-inline'", "'unsafe-eval'");
|
||||
|
||||
for (const directive of ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"]) {
|
||||
pushDirective(directive, "blob:", "data:", "vencord:");
|
||||
pushDirective(directive, "blob:", "data:", "vencord:", "vesktop:");
|
||||
}
|
||||
|
||||
for (const [host, directives] of Object.entries(NativeSettings.store.customCspRules)) {
|
||||
|
||||
7
src/modules.d.ts
vendored
7
src/modules.d.ts
vendored
@@ -16,8 +16,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/// <reference types="standalone-electron-types"/>
|
||||
|
||||
declare module "~plugins" {
|
||||
const plugins: Record<string, import("./utils/types").Plugin>;
|
||||
export default plugins;
|
||||
@@ -28,11 +26,6 @@ declare module "~plugins" {
|
||||
export const ExcludedPlugins: Record<string, "web" | "discordDesktop" | "vesktop" | "desktop" | "dev">;
|
||||
}
|
||||
|
||||
declare module "~pluginNatives" {
|
||||
const pluginNatives: Record<string, Record<string, (event: Electron.IpcMainInvokeEvent, ...args: unknown[]) => unknown>>;
|
||||
export default pluginNatives;
|
||||
}
|
||||
|
||||
declare module "~git-hash" {
|
||||
const hash: string;
|
||||
export default hash;
|
||||
|
||||
12
src/nativeModules.d.ts
vendored
Normal file
12
src/nativeModules.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/// <reference types="standalone-electron-types"/>
|
||||
|
||||
declare module "~pluginNatives" {
|
||||
const pluginNatives: Record<string, Record<string, (event: Electron.IpcMainInvokeEvent, ...args: unknown[]) => unknown>>;
|
||||
export default pluginNatives;
|
||||
}
|
||||
87
src/plugins/_api/badges/BadgeModal.tsx
Normal file
87
src/plugins/_api/badges/BadgeModal.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./badgeModal.css";
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Heart } from "@components/Heart";
|
||||
import { Link } from "@components/Link";
|
||||
import { NxCard, NxText } from "@components/NxComponents";
|
||||
import DonateButton from "@components/settings/DonateButton";
|
||||
import { classes } from "@utils/misc";
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||
import { Forms } from "@webpack/common";
|
||||
|
||||
import { NxSpark } from "./NxSpark";
|
||||
|
||||
const cl = classNameFactory("nx-badge-modal-");
|
||||
|
||||
export function BadgeModal({ badge, props, nxBadge }: { badge: Record<"tooltip" | "badge", string>, props: ModalProps, nxBadge: boolean; }) {
|
||||
return (
|
||||
<ModalRoot {...props} className={classes(cl("root"), !nxBadge ? cl("root-vc") : "")}>
|
||||
<ModalHeader>
|
||||
<Flex style={{ width: "100%", justifyContent: "center" }}>
|
||||
<Forms.FormTitle
|
||||
tag="h2"
|
||||
style={{
|
||||
width: "100%",
|
||||
textAlign: "center",
|
||||
margin: 0
|
||||
}}
|
||||
>
|
||||
{!nxBadge ? <>
|
||||
<Heart />
|
||||
Vencord Donor
|
||||
</> : "Special Badge"}
|
||||
</Forms.FormTitle>
|
||||
</Flex>
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<NxCard variant="grand" className={cl("header")}>
|
||||
<span className={classes(cl("badge"), !nxBadge ? cl("vc-badge") : "")}>
|
||||
<img src={badge.badge} draggable="false"></img>
|
||||
</span>
|
||||
<span className={cl("badge-divider")}></span>
|
||||
<div>
|
||||
<Forms.FormTitle
|
||||
tag="h1"
|
||||
style={{
|
||||
margin: 0
|
||||
}}
|
||||
>
|
||||
{badge.tooltip} {nxBadge ? <NxSpark></NxSpark> : ""}
|
||||
</Forms.FormTitle>
|
||||
<Forms.FormText>
|
||||
{!nxBadge ?
|
||||
"This Badge was given to this user as a special perk for Vencord Donors." :
|
||||
"This Badge was granted to this user by the owner of Nexulien."
|
||||
}
|
||||
</Forms.FormText>
|
||||
</div>
|
||||
</NxCard>
|
||||
<NxCard size="small" className={cl("description")}>
|
||||
<NxText size="small">
|
||||
{!nxBadge ?
|
||||
"Please consider supporting the development of Vencord by becoming a donor! It would mean a lot to them." :
|
||||
"Currently the only way to get a badge is by asking @zoid.one, or getting a PR accepted in the assets repo."
|
||||
}
|
||||
</NxText>
|
||||
</NxCard>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Flex style={{ width: "100%", justifyContent: "center" }}>
|
||||
{!nxBadge ?
|
||||
<DonateButton /> :
|
||||
<Forms.FormText>
|
||||
<Link href="https://github.com/Nexulien/assets">Visit the assets repo</Link>
|
||||
</Forms.FormText>
|
||||
}
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
);
|
||||
}
|
||||
@@ -1,36 +1,34 @@
|
||||
.nx-badge-modal-root {
|
||||
background: linear-gradient(var(--nx-green-background), transparent 75%), var(--modal-background);
|
||||
background-origin: border-box !important;
|
||||
}
|
||||
|
||||
.nx-badge-modal-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 16px;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.nx-badge-modal-badge {
|
||||
display: flex;
|
||||
background: linear-gradient(135deg, #70e, #0f9);
|
||||
|
||||
/* background: linear-gradient(135deg, #70e, #0f9); */
|
||||
border-radius: 50%;
|
||||
height: 96px;
|
||||
width: 96px;
|
||||
min-width: 96px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nx-badge-modal-badge::after {
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--modal-background);;
|
||||
z-index: 1;
|
||||
margin-left: 8px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.nx-badge-modal-badge > img {
|
||||
display: block;
|
||||
height: 64px;
|
||||
width: 64px;
|
||||
z-index: 2;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.nx-badge-modal-header > div {
|
||||
@@ -40,14 +38,21 @@
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.nx-badge-modal-description {
|
||||
padding: 16px;
|
||||
background-color: var(--input-background);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--input-border);
|
||||
color: var(--text-normal);
|
||||
.nx-badge-modal-header > div > div[data-text-variant="text-sm/normal"] {
|
||||
color: var(--text-secondary) !important;
|
||||
}
|
||||
|
||||
.nx-badge-modal-badge.yucky-vencord {
|
||||
background: none;
|
||||
.nx-badge-modal-badge-divider {
|
||||
width: 1px;
|
||||
height: 48px;
|
||||
background-color: var(--border-subtle);
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.nx-badge-modal-root-vc {
|
||||
background: linear-gradient(var(--nx-vc-pink-background), transparent 50%), var(--modal-background);
|
||||
}
|
||||
|
||||
.nx-badge-modal-root-vc .nx-badge-modal-description {
|
||||
background-color: var(--nx-vc-pink-background) !important;
|
||||
}
|
||||
@@ -17,25 +17,19 @@
|
||||
*/
|
||||
|
||||
import "./fixDiscordBadgePadding.css";
|
||||
import "./badgeModal.css";
|
||||
|
||||
import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Heart } from "@components/Heart";
|
||||
import { Link } from "@components/Link";
|
||||
import DonateButton from "@components/settings/DonateButton";
|
||||
import { openContributorModal } from "@components/settings/tabs";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { copyWithToast, shouldShowContributorBadge } from "@utils/misc";
|
||||
import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal";
|
||||
import { closeModal, openModal } from "@utils/modal";
|
||||
import definePlugin from "@utils/types";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { ContextMenuApi, Forms, Menu, Toasts, UserStore } from "@webpack/common";
|
||||
import { ContextMenuApi, Menu, Toasts, UserStore } from "@webpack/common";
|
||||
|
||||
import { NxSpark } from "./NxSpark";
|
||||
import { BadgeModal } from "./BadgeModal";
|
||||
|
||||
|
||||
const CONTRIBUTOR_BADGE = "https://raw.githubusercontent.com/Nexulien/assets/main/badges/contributor.png";
|
||||
@@ -124,6 +118,13 @@ export default definePlugin({
|
||||
replace: "...$self.getBadgeMouseEventHandlers($1),$&"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: "profileCardUsernameRow,children:",
|
||||
replacement: {
|
||||
match: /badges:(\i)(?<=displayProfile:(\i).+?)/,
|
||||
replace: "badges:[...$self.getBadges($2),...$1]"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -189,7 +190,6 @@ export default definePlugin({
|
||||
},
|
||||
|
||||
getDonorBadges(userId: string) {
|
||||
if (userId !== "343383572805058560") {
|
||||
return DonorBadges[userId]?.map(badge => ({
|
||||
image: badge.badge,
|
||||
description: badge.tooltip,
|
||||
@@ -206,58 +206,11 @@ export default definePlugin({
|
||||
closeModal(modalKey);
|
||||
VencordNative.native.openExternal("https://github.com/sponsors/Vendicated");
|
||||
}}>
|
||||
<ModalRoot {...props}>
|
||||
<ModalHeader>
|
||||
<Flex style={{ width: "100%", justifyContent: "center" }}>
|
||||
<Forms.FormTitle
|
||||
tag="h2"
|
||||
style={{
|
||||
width: "100%",
|
||||
textAlign: "center",
|
||||
margin: 0
|
||||
}}
|
||||
>
|
||||
<Heart />
|
||||
Vencord Donor
|
||||
</Forms.FormTitle>
|
||||
</Flex>
|
||||
</ModalHeader>
|
||||
<ModalContent className={Margins.bottom16}>
|
||||
<div className="nx-badge-modal-header">
|
||||
<span className="nx-badge-modal-badge yucky-vencord">
|
||||
<img src={badge.badge} draggable="false"></img>
|
||||
</span>
|
||||
<div>
|
||||
<Forms.FormTitle
|
||||
tag="h1"
|
||||
style={{
|
||||
margin: 0
|
||||
}}
|
||||
>
|
||||
{badge.tooltip}
|
||||
</Forms.FormTitle>
|
||||
<Forms.FormText>
|
||||
This Badge was given to this user as a special perk for Vencord Donors.
|
||||
</Forms.FormText>
|
||||
</div>
|
||||
</div>
|
||||
<div className="nx-badge-modal-description">
|
||||
<Forms.FormText>
|
||||
Please consider supporting the development of Vencord by becoming a donor. It would mean a lot to them!
|
||||
</Forms.FormText>
|
||||
</div>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Flex style={{ width: "100%", justifyContent: "center" }}>
|
||||
<DonateButton />
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<BadgeModal badge={badge} props={props} nxBadge={false}></BadgeModal>
|
||||
</ErrorBoundary>
|
||||
));
|
||||
},
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
getNexulienBadges(userId: string) {
|
||||
@@ -280,54 +233,7 @@ export default definePlugin({
|
||||
closeModal(modalKey);
|
||||
VencordNative.native.openExternal("https://github.com/Nexulien/assets/blob/main/badges.json");
|
||||
}}>
|
||||
<ModalRoot {...props}>
|
||||
<ModalHeader>
|
||||
<Flex style={{ width: "100%", justifyContent: "center" }}>
|
||||
<Forms.FormTitle
|
||||
tag="h2"
|
||||
style={{
|
||||
width: "100%",
|
||||
textAlign: "center",
|
||||
margin: 0
|
||||
}}
|
||||
>
|
||||
Special Badge
|
||||
</Forms.FormTitle>
|
||||
</Flex>
|
||||
</ModalHeader>
|
||||
<ModalContent className={Margins.bottom16}>
|
||||
<div className="nx-badge-modal-header">
|
||||
<span className="nx-badge-modal-badge">
|
||||
<img src={badge.badge} draggable="false"></img>
|
||||
</span>
|
||||
<div>
|
||||
<Forms.FormTitle
|
||||
tag="h1"
|
||||
style={{
|
||||
margin: 0
|
||||
}}
|
||||
>
|
||||
{badge.tooltip} <NxSpark />
|
||||
</Forms.FormTitle>
|
||||
<Forms.FormText>
|
||||
This Badge was granted to this user by the owner of Nexulien.
|
||||
</Forms.FormText>
|
||||
</div>
|
||||
</div>
|
||||
<div className="nx-badge-modal-description">
|
||||
<Forms.FormText>
|
||||
Currently the only way to get one is by asking @thezoidmaster, or getting a PR accepted in the assets repo.
|
||||
</Forms.FormText>
|
||||
</div>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Flex style={{ width: "100%", justifyContent: "center" }}>
|
||||
<Forms.FormText>
|
||||
<Link href="https://github.com/Nexulien/assets">Visit the assets repo</Link>
|
||||
</Forms.FormText>
|
||||
</Flex>
|
||||
</ModalFooter>
|
||||
</ModalRoot>
|
||||
<BadgeModal badge={badge} props={props} nxBadge={true}></BadgeModal>
|
||||
</ErrorBoundary>
|
||||
));
|
||||
},
|
||||
|
||||
@@ -71,6 +71,15 @@ export default definePlugin({
|
||||
}
|
||||
],
|
||||
|
||||
// The TRACK event takes an optional `resolve` property that is called when the tracking event was submitted to the server.
|
||||
// A few spots in Discord await this callback before continuing (most notably the Voice Debug Logging toggle).
|
||||
// Since we NOOP the AnalyticsActionHandlers module, there is no handler for the TRACK event, so we have to handle it ourselves
|
||||
flux: {
|
||||
TRACK(event) {
|
||||
event?.resolve?.();
|
||||
}
|
||||
},
|
||||
|
||||
startAt: StartAt.Init,
|
||||
start() {
|
||||
// Sentry is initialized in its own WebpackInstance.
|
||||
|
||||
@@ -39,7 +39,7 @@ export default definePlugin({
|
||||
find: ".versionHash",
|
||||
replacement: [
|
||||
{
|
||||
match: /\[\(0,\i\.jsxs?\)\((.{1,10}),(\{[^{}}]+\{.{0,20}.versionHash,.+?\})\)," "/,
|
||||
match: /\.info.+?\[\(0,\i\.jsxs?\)\((.{1,10}),(\{[^{}}]+\{.{0,20}.versionHash,.+?\})\)," "/,
|
||||
replace: (m, component, props) => {
|
||||
props = props.replace(/children:\[.+\]/, "");
|
||||
return `${m},$self.makeInfoElements(${component}, ${props})`;
|
||||
@@ -67,8 +67,9 @@ export default definePlugin({
|
||||
{
|
||||
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
|
||||
replacement: {
|
||||
match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.\i\).*?(\i\.\i)\.open\()/,
|
||||
replace: "$2.open($1);return;"
|
||||
// Skip the check Discord performs to make sure the section being selected in the user settings context menu is valid
|
||||
match: /(?<=function\((\i),(\i),\i\)\{)(?=let \i=Object.values\(\i\.\i\).+?(\(0,\i\.openUserSettings\))\()/,
|
||||
replace: (_, settingsPanel, section, openUserSettings) => `${openUserSettings}(${settingsPanel},{section:${section}});return;`
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -80,37 +81,37 @@ export default definePlugin({
|
||||
{
|
||||
section: SectionTypes.HEADER,
|
||||
label: "Nexulien",
|
||||
className: "nx-settings-header"
|
||||
className: "vc-settings-header"
|
||||
},
|
||||
{
|
||||
section: "settings/tabs",
|
||||
label: "Settings",
|
||||
element: VencordTab,
|
||||
className: "nx-settings"
|
||||
className: "vc-settings"
|
||||
},
|
||||
{
|
||||
section: "NexulienPlugins",
|
||||
label: "Plugins",
|
||||
element: PluginsTab,
|
||||
className: "nx-plugins"
|
||||
className: "vc-plugins"
|
||||
},
|
||||
{
|
||||
section: "NexulienThemes",
|
||||
label: "Themes",
|
||||
element: ThemesTab,
|
||||
className: "nx-themes"
|
||||
className: "vc-themes"
|
||||
},
|
||||
!IS_UPDATER_DISABLED && {
|
||||
section: "NexulienUpdater",
|
||||
label: "Updater",
|
||||
element: UpdaterTab,
|
||||
className: "nx-updater"
|
||||
className: "vc-updater"
|
||||
},
|
||||
{
|
||||
section: "NexulienNotifications",
|
||||
label: "Notifications",
|
||||
element: NotificationsTab,
|
||||
className: "nx-settings"
|
||||
className: "nx-notifications"
|
||||
},
|
||||
{
|
||||
section: "VencordCloud",
|
||||
|
||||
@@ -7,49 +7,12 @@
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs, IS_MAC } from "@utils/constants";
|
||||
import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types";
|
||||
import { Activity, ActivityAssets, ActivityButton } from "@vencord/discord-types";
|
||||
import { ActivityFlags, ActivityStatusDisplayType, ActivityType } from "@vencord/discord-types/enums";
|
||||
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
|
||||
|
||||
const Native = VencordNative.pluginHelpers.AppleMusicRichPresence as PluginNative<typeof import("./native")>;
|
||||
|
||||
interface ActivityAssets {
|
||||
large_image?: string;
|
||||
large_text?: string;
|
||||
small_image?: string;
|
||||
small_text?: string;
|
||||
}
|
||||
|
||||
interface ActivityButton {
|
||||
label: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface Activity {
|
||||
state?: string;
|
||||
details?: string;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
end?: number;
|
||||
};
|
||||
assets?: ActivityAssets;
|
||||
buttons?: Array<string>;
|
||||
name: string;
|
||||
application_id: string;
|
||||
metadata?: {
|
||||
button_urls?: Array<string>;
|
||||
};
|
||||
type: number;
|
||||
flags: number;
|
||||
}
|
||||
|
||||
const enum ActivityType {
|
||||
PLAYING = 0,
|
||||
LISTENING = 2,
|
||||
}
|
||||
|
||||
const enum ActivityFlag {
|
||||
INSTANCE = 1 << 0,
|
||||
}
|
||||
|
||||
export interface TrackData {
|
||||
name: string;
|
||||
album?: string;
|
||||
@@ -90,6 +53,25 @@ const settings = definePluginSettings({
|
||||
{ label: "Listening", value: ActivityType.LISTENING }
|
||||
],
|
||||
},
|
||||
statusDisplayType: {
|
||||
description: "Show the track / artist name in the member list",
|
||||
type: OptionType.SELECT,
|
||||
options: [
|
||||
{
|
||||
label: "Don't show (shows generic listening message)",
|
||||
value: "off",
|
||||
default: true
|
||||
},
|
||||
{
|
||||
label: "Show artist name",
|
||||
value: "artist"
|
||||
},
|
||||
{
|
||||
label: "Show track name",
|
||||
value: "track"
|
||||
}
|
||||
]
|
||||
},
|
||||
refreshInterval: {
|
||||
type: OptionType.SLIDER,
|
||||
description: "The interval between activity refreshes (seconds)",
|
||||
@@ -258,7 +240,12 @@ export default definePlugin({
|
||||
metadata: !isRadio && buttons.length ? { button_urls: buttons.map(v => v.url) } : undefined,
|
||||
|
||||
type: settings.store.activityType,
|
||||
flags: ActivityFlag.INSTANCE,
|
||||
status_display_type: {
|
||||
"off": ActivityStatusDisplayType.NAME,
|
||||
"artist": ActivityStatusDisplayType.STATE,
|
||||
"track": ActivityStatusDisplayType.DETAILS
|
||||
}[settings.store.statusDisplayType],
|
||||
flags: ActivityFlags.INSTANCE,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { canonicalizeMatch } from "@utils/patches";
|
||||
import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent";
|
||||
import { execFile } from "child_process";
|
||||
import { promisify } from "util";
|
||||
|
||||
@@ -26,24 +26,6 @@ interface RemoteData {
|
||||
|
||||
let cachedRemoteData: { id: string, data: RemoteData; } | { id: string, failures: number; } | null = null;
|
||||
|
||||
const APPLE_MUSIC_BUNDLE_REGEX = /<script type="module" crossorigin src="([a-zA-Z0-9.\-/]+)"><\/script>/;
|
||||
const APPLE_MUSIC_TOKEN_REGEX = canonicalizeMatch(/\b(\i)="([A-Za-z0-9-_]*\.[A-Za-z0-9-_]*\.[A-Za-z0-9-_]*)"(?=.+?Bearer \$\{\1\})/);
|
||||
|
||||
let cachedToken: string | undefined = undefined;
|
||||
|
||||
const getToken = async () => {
|
||||
if (cachedToken) return cachedToken;
|
||||
|
||||
const html = await fetch("https://music.apple.com/").then(r => r.text());
|
||||
const bundleUrl = new URL(html.match(APPLE_MUSIC_BUNDLE_REGEX)![1], "https://music.apple.com/");
|
||||
|
||||
const bundle = await fetch(bundleUrl).then(r => r.text());
|
||||
const token = bundle.match(APPLE_MUSIC_TOKEN_REGEX)![2];
|
||||
|
||||
cachedToken = token;
|
||||
return token;
|
||||
};
|
||||
|
||||
async function fetchRemoteData({ id, name, artist, album }: { id: string, name: string, artist: string, album: string; }) {
|
||||
if (id === cachedRemoteData?.id) {
|
||||
if ("data" in cachedRemoteData) return cachedRemoteData.data;
|
||||
@@ -51,36 +33,34 @@ async function fetchRemoteData({ id, name, artist, album }: { id: string, name:
|
||||
}
|
||||
|
||||
try {
|
||||
const dataUrl = new URL("https://amp-api-edge.music.apple.com/v1/catalog/us/search");
|
||||
dataUrl.searchParams.set("platform", "web");
|
||||
dataUrl.searchParams.set("l", "en-US");
|
||||
dataUrl.searchParams.set("limit", "1");
|
||||
dataUrl.searchParams.set("with", "serverBubbles");
|
||||
dataUrl.searchParams.set("types", "songs");
|
||||
const dataUrl = new URL("https://itunes.apple.com/search");
|
||||
dataUrl.searchParams.set("term", `${name} ${artist} ${album}`);
|
||||
dataUrl.searchParams.set("include[songs]", "artists");
|
||||
|
||||
const token = await getToken();
|
||||
dataUrl.searchParams.set("media", "music");
|
||||
dataUrl.searchParams.set("entity", "song");
|
||||
|
||||
const songData = await fetch(dataUrl, {
|
||||
headers: {
|
||||
"accept": "*/*",
|
||||
"accept-language": "en-US,en;q=0.9",
|
||||
"authorization": `Bearer ${token}`,
|
||||
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
|
||||
"origin": "https://music.apple.com",
|
||||
"user-agent": VENCORD_USER_AGENT,
|
||||
},
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => data.results.song.data[0]);
|
||||
.then(data => data.results.find(song => song.collectionName === album) || data.results[0]);
|
||||
|
||||
const artistArtworkURL = await fetch(songData.artistViewUrl)
|
||||
.then(r => r.text())
|
||||
.then(data => {
|
||||
const match = data.match(/<meta property="og:image" content="(.+?)">/);
|
||||
return match ? match[1].replace(/[0-9]+x.+/, "220x220bb-60.png") : undefined;
|
||||
})
|
||||
.catch(() => void 0);
|
||||
|
||||
cachedRemoteData = {
|
||||
id,
|
||||
data: {
|
||||
appleMusicLink: songData.attributes.url,
|
||||
songLink: `https://song.link/i/${songData.id}`,
|
||||
albumArtwork: songData.attributes.artwork.url.replace("{w}x{h}", "512x512"),
|
||||
artistArtwork: songData.relationships.artists.data[0].attributes.artwork.url.replace("{w}x{h}", "512x512"),
|
||||
appleMusicLink: songData.trackViewUrl,
|
||||
songLink: `https://song.link/i/${new URL(songData.trackViewUrl).searchParams.get("i")}`,
|
||||
albumArtwork: (songData.artworkUrl100).replace("100x100", "512x512"),
|
||||
artistArtwork: artistArtworkURL
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ export default definePlugin({
|
||||
|
||||
ws.onmessage = this.handleEvent;
|
||||
|
||||
const connectionSuccessful = await new Promise(res => setTimeout(() => res(ws.readyState === WebSocket.OPEN), 1000)); // check if open after 1s
|
||||
const connectionSuccessful = await new Promise(res => setTimeout(() => res(ws.readyState === WebSocket.OPEN), 5000)); // check if open after 5s
|
||||
if (!connectionSuccessful) {
|
||||
showNotice("Failed to connect to arRPC, is it running?", "Retry", () => { // show notice about failure to connect, with retry/ignore
|
||||
popNotice();
|
||||
|
||||
5
src/plugins/autoDndWhilePlaying.discordDesktop/README.md
Normal file
5
src/plugins/autoDndWhilePlaying.discordDesktop/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# AutoDNDWhilePlaying
|
||||
|
||||
This plugin automatically updates your online status (online, idle, dnd) when launching games.
|
||||
|
||||
It will change your status back to the prior status when the game is closed.
|
||||
61
src/plugins/autoDndWhilePlaying.discordDesktop/index.ts
Normal file
61
src/plugins/autoDndWhilePlaying.discordDesktop/index.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { getUserSettingLazy } from "@api/UserSettings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
let savedStatus: string | null;
|
||||
|
||||
const StatusSettings = getUserSettingLazy<string>("status", "status")!;
|
||||
|
||||
const settings = definePluginSettings({
|
||||
statusToSet: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Status to set while playing a game",
|
||||
options: [
|
||||
{
|
||||
label: "Online",
|
||||
value: "online",
|
||||
},
|
||||
{
|
||||
label: "Idle",
|
||||
value: "idle",
|
||||
},
|
||||
{
|
||||
label: "Do Not Disturb",
|
||||
value: "dnd",
|
||||
default: true
|
||||
},
|
||||
{
|
||||
label: "Invisible",
|
||||
value: "invisible",
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "AutoDNDWhilePlaying",
|
||||
description: "Automatically updates your online status (online, idle, dnd) when launching games",
|
||||
authors: [Devs.thororen],
|
||||
settings,
|
||||
flux: {
|
||||
RUNNING_GAMES_CHANGE({ games }) {
|
||||
const status = StatusSettings.getSetting();
|
||||
|
||||
if (games.length > 0) {
|
||||
if (status !== settings.store.statusToSet) {
|
||||
savedStatus = status;
|
||||
StatusSettings.updateSetting(settings.store.statusToSet);
|
||||
}
|
||||
} else if (savedStatus && savedStatus !== settings.store.statusToSet) {
|
||||
StatusSettings.updateSetting(savedStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -21,24 +21,39 @@ import { findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { Animations, useStateFromStores } from "@webpack/common";
|
||||
import type { CSSProperties } from "react";
|
||||
|
||||
import { ExpandedGuildFolderStore, settings } from ".";
|
||||
import { ExpandedGuildFolderStore, settings, SortedGuildStore } from ".";
|
||||
|
||||
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
||||
const GuildsBar = findComponentByCodeLazy('("guildsnav")');
|
||||
|
||||
function getExpandedFolderIds() {
|
||||
const expandedFolders = ExpandedGuildFolderStore.getExpandedFolders();
|
||||
const folders = SortedGuildStore.getGuildFolders();
|
||||
|
||||
const expandedFolderIds = new Set<string>();
|
||||
|
||||
for (const folder of folders) {
|
||||
if (expandedFolders.has(folder.folderId) && folder.guildIds?.length) {
|
||||
expandedFolderIds.add(folder.folderId);
|
||||
}
|
||||
}
|
||||
|
||||
return expandedFolderIds;
|
||||
}
|
||||
|
||||
export default ErrorBoundary.wrap(guildsBarProps => {
|
||||
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
||||
const expandedFolderIds = useStateFromStores([ExpandedGuildFolderStore, SortedGuildStore], () => getExpandedFolderIds());
|
||||
const isFullscreen = useStateFromStores([ChannelRTCStore], () => ChannelRTCStore.isFullscreenInContext());
|
||||
|
||||
const Sidebar = (
|
||||
<GuildsBar
|
||||
{...guildsBarProps}
|
||||
isBetterFolders={true}
|
||||
betterFoldersExpandedIds={expandedFolders}
|
||||
betterFoldersExpandedIds={expandedFolderIds}
|
||||
/>
|
||||
);
|
||||
|
||||
const visible = !!expandedFolders.size;
|
||||
const visible = !!expandedFolderIds.size;
|
||||
const guilds = document.querySelector(guildsBarProps.className.split(" ").map(c => `.${c}`).join(""));
|
||||
|
||||
// We need to display none if we are in fullscreen. Yes this seems horrible doing with css, but it's literally how Discord does it.
|
||||
|
||||
@@ -22,7 +22,7 @@ import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
||||
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||
import { FluxDispatcher } from "@webpack/common";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
@@ -35,8 +35,7 @@ enum FolderIconDisplay {
|
||||
}
|
||||
|
||||
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
||||
const SortedGuildStore = findStoreLazy("SortedGuildStore");
|
||||
const GuildsTree = findLazy(m => m.prototype?.moveNextTo);
|
||||
export const SortedGuildStore = findStoreLazy("SortedGuildStore");
|
||||
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
||||
|
||||
let lastGuildId = null as string | null;
|
||||
|
||||
@@ -31,7 +31,7 @@ import { fetchNamesFromDataStore, getDefaultName, GetOsColor, GetPlatformIcon, s
|
||||
const AuthSessionsStore = findStoreLazy("AuthSessionsStore");
|
||||
const UserSettingsModal = findByPropsLazy("saveAccountChanges", "open");
|
||||
|
||||
const TimestampClasses = findByPropsLazy("timestampTooltip", "blockquoteContainer");
|
||||
const TimestampClasses = findByPropsLazy("timestamp", "blockquoteContainer");
|
||||
const SessionIconClasses = findByPropsLazy("sessionIcon");
|
||||
|
||||
const BlobMask = findComponentByCodeLazy("!1,lowerBadgeSize:");
|
||||
@@ -92,7 +92,7 @@ export default definePlugin({
|
||||
<span>{title}</span>
|
||||
{(savedSession == null || savedSession.isNew) && (
|
||||
<div
|
||||
className="nx-plugins-badge"
|
||||
className="vc-plugins-badge"
|
||||
style={{
|
||||
backgroundColor: "#ED4245",
|
||||
marginLeft: "2px"
|
||||
@@ -108,7 +108,7 @@ export default definePlugin({
|
||||
|
||||
renderTimestamp: ErrorBoundary.wrap(({ session, timeLabel }: { session: Session, timeLabel: string; }) => {
|
||||
return (
|
||||
<Tooltip text={session.approx_last_used_time.toLocaleString()} tooltipClassName={TimestampClasses.timestampTooltip}>
|
||||
<Tooltip text={session.approx_last_used_time.toLocaleString()}>
|
||||
{props => (
|
||||
<span {...props} className={TimestampClasses.timestamp}>
|
||||
{timeLabel}
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function PluginsSubmenu() {
|
||||
return (
|
||||
<>
|
||||
<Menu.MenuControlItem
|
||||
id="nx-plugins-search"
|
||||
id="vc-plugins-search"
|
||||
control={(props, ref) => (
|
||||
<Menu.MenuSearchControl
|
||||
{...props}
|
||||
|
||||
@@ -32,7 +32,8 @@ const settings = definePluginSettings({
|
||||
organizeMenu: {
|
||||
description: "Organizes the settings cog context menu into categories",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true
|
||||
default: true,
|
||||
restartNeeded: true
|
||||
},
|
||||
eagerLoad: {
|
||||
description: "Removes the loading delay when opening the menu for the first time",
|
||||
@@ -119,12 +120,14 @@ export default definePlugin({
|
||||
},
|
||||
predicate: () => settings.store.eagerLoad
|
||||
},
|
||||
{ // Settings cog context menu
|
||||
{
|
||||
// Settings cog context menu
|
||||
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
|
||||
replacement: [
|
||||
{
|
||||
match: /(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/,
|
||||
replace: "$self.wrapMenu($1)"
|
||||
match: /=\[\];return (\i)(?=\.forEach)/,
|
||||
replace: "=$self.wrapMap([]);return $self.transformSettingsEntries($1)",
|
||||
predicate: () => settings.store.organizeMenu
|
||||
},
|
||||
{
|
||||
match: /case \i\.\i\.DEVELOPER_OPTIONS:return \i;/,
|
||||
@@ -151,9 +154,7 @@ export default definePlugin({
|
||||
return <Layer {...props} />;
|
||||
},
|
||||
|
||||
wrapMenu(list: SettingsEntry[]) {
|
||||
if (!settings.store.organizeMenu) return list;
|
||||
|
||||
transformSettingsEntries(list: SettingsEntry[]) {
|
||||
const items = [{ label: null as string | null, items: [] as SettingsEntry[] }];
|
||||
|
||||
for (const item of list) {
|
||||
@@ -166,15 +167,13 @@ export default definePlugin({
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
filter(predicate: (item: SettingsEntry) => boolean) {
|
||||
for (const category of items) {
|
||||
category.items = category.items.filter(predicate);
|
||||
}
|
||||
return this;
|
||||
return items;
|
||||
},
|
||||
map(render: (item: SettingsEntry) => ReactElement<any>) {
|
||||
return items
|
||||
|
||||
wrapMap(toWrap: any[]) {
|
||||
// @ts-expect-error
|
||||
toWrap.map = function (render: (item: SettingsEntry) => ReactElement<any>) {
|
||||
return this
|
||||
.filter(a => a.items.length > 0)
|
||||
.map(({ label, items }) => {
|
||||
const children = items.map(render);
|
||||
@@ -184,7 +183,6 @@ export default definePlugin({
|
||||
key={label}
|
||||
id={label.replace(/\W/, "_")}
|
||||
label={label}
|
||||
action={children[0].props.action}
|
||||
>
|
||||
{children}
|
||||
</Menu.MenuItem>
|
||||
@@ -193,7 +191,8 @@ export default definePlugin({
|
||||
return children;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return toWrap;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.align-chat-input [class*="panels"] [class*="inner_"],
|
||||
.align-chat-input [class*="rtcConnectionStatus_"] {
|
||||
height: fit-content;
|
||||
[class*="panels"] [class*="inner_"],
|
||||
[class*="rtcConnectionStatus_"] {
|
||||
height: fit-content !important;
|
||||
}
|
||||
@@ -95,6 +95,6 @@ export default definePlugin({
|
||||
deps: [channelId]
|
||||
});
|
||||
|
||||
return <p style={{ margin: 0 }}>Connected for <span style={{ fontFamily: "var(--font-code)" }}>{formatDuration(time)}</span></p>;
|
||||
return <p style={{ margin: 0, fontFamily: "var(--font-code)" }}>{formatDuration(time)}</p>;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -27,6 +27,8 @@ import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Activity } from "@vencord/discord-types";
|
||||
import { ActivityType } from "@vencord/discord-types/enums";
|
||||
import { findByCodeLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, React, UserStore } from "@webpack/common";
|
||||
|
||||
@@ -39,41 +41,7 @@ async function getApplicationAsset(key: string): Promise<string> {
|
||||
return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0];
|
||||
}
|
||||
|
||||
interface ActivityAssets {
|
||||
large_image?: string;
|
||||
large_text?: string;
|
||||
small_image?: string;
|
||||
small_text?: string;
|
||||
}
|
||||
|
||||
interface Activity {
|
||||
state?: string;
|
||||
details?: string;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
end?: number;
|
||||
};
|
||||
assets?: ActivityAssets;
|
||||
buttons?: Array<string>;
|
||||
name: string;
|
||||
application_id: string;
|
||||
metadata?: {
|
||||
button_urls?: Array<string>;
|
||||
};
|
||||
type: ActivityType;
|
||||
url?: string;
|
||||
flags: number;
|
||||
}
|
||||
|
||||
const enum ActivityType {
|
||||
PLAYING = 0,
|
||||
STREAMING = 1,
|
||||
LISTENING = 2,
|
||||
WATCHING = 3,
|
||||
COMPETING = 5
|
||||
}
|
||||
|
||||
const enum TimestampMode {
|
||||
export const enum TimestampMode {
|
||||
NONE,
|
||||
NOW,
|
||||
TIME,
|
||||
|
||||
@@ -89,7 +89,8 @@ function CreateDecorationModal(props: ModalProps) {
|
||||
<div className={cl("create-decoration-modal-form-preview-container")}>
|
||||
<div className={cl("create-decoration-modal-form")}>
|
||||
{error !== null && <Text color="text-danger" variant="text-xs/normal">{error.message}</Text>}
|
||||
<Forms.FormSection title="File">
|
||||
<section>
|
||||
<Forms.FormTitle tag="h5">File</Forms.FormTitle>
|
||||
<FileUpload
|
||||
filename={file?.name}
|
||||
placeholder="Choose a file"
|
||||
@@ -100,8 +101,9 @@ function CreateDecorationModal(props: ModalProps) {
|
||||
<Forms.FormText className={Margins.top8}>
|
||||
File should be APNG or PNG.
|
||||
</Forms.FormText>
|
||||
</Forms.FormSection>
|
||||
<Forms.FormSection title="Name">
|
||||
</section>
|
||||
<section>
|
||||
<Forms.FormTitle tag="h5">Name</Forms.FormTitle>
|
||||
<TextInput
|
||||
placeholder="Companion Cube"
|
||||
value={name}
|
||||
@@ -110,7 +112,7 @@ function CreateDecorationModal(props: ModalProps) {
|
||||
<Forms.FormText className={Margins.top8}>
|
||||
This name will be used when referring to this decoration.
|
||||
</Forms.FormText>
|
||||
</Forms.FormSection>
|
||||
</section>
|
||||
</div>
|
||||
<div>
|
||||
<AvatarDecorationModalPreview
|
||||
|
||||
@@ -113,7 +113,7 @@ function SettingsAboutComponent() {
|
||||
const [color2, setColor2] = useState(existingColors[1]);
|
||||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<section>
|
||||
<Forms.FormTitle tag="h3">Usage</Forms.FormTitle>
|
||||
<Forms.FormText>
|
||||
After enabling this plugin, you will see custom colors in
|
||||
@@ -191,7 +191,7 @@ function SettingsAboutComponent() {
|
||||
/>
|
||||
</div>
|
||||
</Forms.FormText>
|
||||
</Forms.FormSection>);
|
||||
</section>);
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Fix Images Quality
|
||||
|
||||
Prevents images from being loaded as webp, which can cause quality loss
|
||||
Improves quality of images in chat by forcing png format.
|
||||
|
||||
This plugin does not change how others see your images!
|
||||
|
||||
@@ -9,14 +9,14 @@ import definePlugin from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "FixImagesQuality",
|
||||
description: "Prevents images from being loaded as webp, which can cause quality loss",
|
||||
description: "Improves quality of images in chat by forcing png format",
|
||||
authors: [Devs.Nuckyz],
|
||||
patches: [
|
||||
{
|
||||
find: ".handleImageLoad)",
|
||||
replacement: {
|
||||
match: /(?<=null;return )\i\.\i&&\(\i\|\|!\i\.isAnimated.+?:(?=\i&&\(\i="png"\))/,
|
||||
replace: ""
|
||||
match: /(?<=\i=)"webp"/,
|
||||
replace: '"png"'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -60,7 +60,7 @@ function makeIcon(showCurrentGame?: boolean) {
|
||||
};
|
||||
}
|
||||
|
||||
function GameActivityToggleButton() {
|
||||
function GameActivityToggleButton(props: { nameplate?: any; }) {
|
||||
const showCurrentGame = ShowCurrentGame.useSetting();
|
||||
|
||||
return (
|
||||
@@ -70,6 +70,7 @@ function GameActivityToggleButton() {
|
||||
role="switch"
|
||||
aria-checked={!showCurrentGame}
|
||||
redGlow={!showCurrentGame}
|
||||
plated={props?.nameplate != null}
|
||||
onClick={() => ShowCurrentGame.updateSetting(old => !old)}
|
||||
/>
|
||||
);
|
||||
@@ -97,7 +98,7 @@ export default definePlugin({
|
||||
find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
|
||||
replacement: {
|
||||
match: /className:\i\.buttons,.{0,50}children:\[/,
|
||||
replace: "$&$self.GameActivityToggleButton(),"
|
||||
replace: "$&$self.GameActivityToggleButton(arguments[0]),"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -131,7 +131,7 @@ function IdsListComponent(props: { setValue: (value: string) => void; }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<section>
|
||||
<Forms.FormTitle tag="h3">Filter List</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom8}>Comma separated list of activity IDs to filter (Useful for filtering specific RPC activities and CustomRPC</Forms.FormText>
|
||||
<TextInput
|
||||
@@ -140,7 +140,7 @@ function IdsListComponent(props: { setValue: (value: string) => void; }) {
|
||||
onChange={handleChange}
|
||||
placeholder="235834946571337729, 343383572805058560"
|
||||
/>
|
||||
</Forms.FormSection>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ export default definePlugin({
|
||||
},
|
||||
// Sections header
|
||||
{
|
||||
find: "#{intl::FRIENDS_SECTION_ONLINE}",
|
||||
find: "#{intl::FRIENDS_SECTION_ONLINE}),className:",
|
||||
replacement: {
|
||||
match: /,{id:(\i\.\i)\.PENDING,show:.+?className:(\i\.item)/,
|
||||
replace: (rest, relationShipTypes, className) => `,{id:${relationShipTypes}.IMPLICIT,show:true,className:${className},content:"Implicit"}${rest}`
|
||||
|
||||
@@ -222,7 +222,7 @@ export function subscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof Flux
|
||||
for (const [event, handler] of Object.entries(p.flux)) {
|
||||
const wrappedHandler = p.flux[event] = function () {
|
||||
try {
|
||||
const res = handler.apply(p, arguments as any);
|
||||
const res = handler!.apply(p, arguments as any);
|
||||
return res instanceof Promise
|
||||
? res.catch(e => logger.error(`${p.name}: Error while handling ${event}\n`, e))
|
||||
: res;
|
||||
@@ -242,7 +242,7 @@ export function unsubscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof Fl
|
||||
|
||||
logger.debug("Unsubscribing from flux events of plugin", p.name);
|
||||
for (const [event, handler] of Object.entries(p.flux)) {
|
||||
fluxDispatcher.unsubscribe(event as FluxEvents, handler);
|
||||
fluxDispatcher.unsubscribe(event as FluxEvents, handler!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { FormSwitch } from "@components/FormSwitch";
|
||||
import { insertTextIntoChatInputBox } from "@utils/discord";
|
||||
import {
|
||||
ModalContent,
|
||||
@@ -25,7 +26,7 @@ import {
|
||||
ModalRoot,
|
||||
openModal,
|
||||
} from "@utils/modal";
|
||||
import { Button, Forms, React, Switch, TextInput } from "@webpack/common";
|
||||
import { Button, Forms, React, TextInput } from "@webpack/common";
|
||||
|
||||
import { encrypt } from "../index";
|
||||
|
||||
@@ -65,14 +66,13 @@ function EncModal(props: ModalProps) {
|
||||
setPassword(e);
|
||||
}}
|
||||
/>
|
||||
<Switch
|
||||
<FormSwitch
|
||||
title="Don't use a Cover"
|
||||
value={noCover}
|
||||
onChange={(e: boolean) => {
|
||||
setNoCover(e);
|
||||
}}
|
||||
>
|
||||
Don't use a Cover
|
||||
</Switch>
|
||||
/>
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter>
|
||||
|
||||
@@ -21,40 +21,11 @@ import { Link } from "@components/Link";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Activity, ActivityAssets, ActivityButton } from "@vencord/discord-types";
|
||||
import { ActivityFlags, ActivityStatusDisplayType, ActivityType } from "@vencord/discord-types/enums";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
|
||||
|
||||
interface ActivityAssets {
|
||||
large_image?: string;
|
||||
large_text?: string;
|
||||
small_image?: string;
|
||||
small_text?: string;
|
||||
}
|
||||
|
||||
|
||||
interface ActivityButton {
|
||||
label: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface Activity {
|
||||
state: string;
|
||||
details?: string;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
};
|
||||
assets?: ActivityAssets;
|
||||
buttons?: Array<string>;
|
||||
name: string;
|
||||
application_id: string;
|
||||
status_display_type?: number;
|
||||
metadata?: {
|
||||
button_urls?: Array<string>;
|
||||
};
|
||||
type: number;
|
||||
flags: number;
|
||||
}
|
||||
|
||||
interface TrackData {
|
||||
name: string;
|
||||
album: string;
|
||||
@@ -63,16 +34,6 @@ interface TrackData {
|
||||
imageUrl?: string;
|
||||
}
|
||||
|
||||
// only relevant enum values
|
||||
const enum ActivityType {
|
||||
PLAYING = 0,
|
||||
LISTENING = 2,
|
||||
}
|
||||
|
||||
const enum ActivityFlag {
|
||||
INSTANCE = 1 << 0,
|
||||
}
|
||||
|
||||
const enum NameFormat {
|
||||
StatusName = "status-name",
|
||||
ArtistFirst = "artist-first",
|
||||
@@ -367,9 +328,9 @@ export default definePlugin({
|
||||
details: trackData.name,
|
||||
state: trackData.artist,
|
||||
status_display_type: {
|
||||
"off": 0,
|
||||
"artist": 1,
|
||||
"track": 2
|
||||
"off": ActivityStatusDisplayType.NAME,
|
||||
"artist": ActivityStatusDisplayType.STATE,
|
||||
"track": ActivityStatusDisplayType.DETAILS
|
||||
}[settings.store.statusDisplayType],
|
||||
assets,
|
||||
|
||||
@@ -379,7 +340,7 @@ export default definePlugin({
|
||||
},
|
||||
|
||||
type: settings.store.useListeningStatus ? ActivityType.LISTENING : ActivityType.PLAYING,
|
||||
flags: ActivityFlag.INSTANCE,
|
||||
flags: ActivityFlags.INSTANCE,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { MessageFlags } from "@vencord/discord-types/enums";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore, WindowStore } from "@webpack/common";
|
||||
import { FluxDispatcher, MessageTypeSets, PermissionsBits, PermissionStore, UserStore, WindowStore } from "@webpack/common";
|
||||
import NoReplyMentionPlugin from "plugins/noReplyMention";
|
||||
|
||||
const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage");
|
||||
@@ -73,7 +74,7 @@ export default definePlugin({
|
||||
WindowStore.removeChangeListener(focusChanged);
|
||||
},
|
||||
|
||||
onMessageClick(msg: any, channel, event) {
|
||||
onMessageClick(msg, channel, event) {
|
||||
const isMe = msg.author.id === UserStore.getCurrentUser().id;
|
||||
if (!isDeletePressed) {
|
||||
if (event.detail < 2) return;
|
||||
@@ -89,8 +90,7 @@ export default definePlugin({
|
||||
} else {
|
||||
if (!settings.store.enableDoubleClickToReply) return;
|
||||
|
||||
const EPHEMERAL = 64;
|
||||
if (msg.hasFlag(EPHEMERAL)) return;
|
||||
if (!MessageTypeSets.REPLYABLE.has(msg.type) || msg.hasFlag(MessageFlags.EPHEMERAL)) return;
|
||||
|
||||
const isShiftPress = event.shiftKey && !settings.store.requireModifier;
|
||||
const shouldMention = Vencord.Plugins.isPluginEnabled(NoReplyMentionPlugin.name)
|
||||
|
||||
@@ -11,7 +11,7 @@ import { isNonNullish } from "@utils/guards";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Message } from "@vencord/discord-types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { SnowflakeUtils, Tooltip } from "@webpack/common";
|
||||
import { AuthenticationStore, SnowflakeUtils, Tooltip } from "@webpack/common";
|
||||
|
||||
type FillValue = ("status-danger" | "status-warning" | "status-positive" | "text-muted");
|
||||
type Fill = [FillValue, FillValue, FillValue];
|
||||
@@ -48,6 +48,11 @@ export default definePlugin({
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show milliseconds",
|
||||
default: false
|
||||
},
|
||||
ignoreSelf: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Don't add indicator to your own messages",
|
||||
default: false
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -91,7 +96,7 @@ export default definePlugin({
|
||||
},
|
||||
|
||||
latencyTooltipData(message: Message) {
|
||||
const { latency, detectDiscordKotlin, showMillis } = this.settings.store;
|
||||
const { latency, detectDiscordKotlin, showMillis, ignoreSelf } = this.settings.store;
|
||||
const { id, nonce } = message;
|
||||
|
||||
// Message wasn't received through gateway
|
||||
@@ -100,6 +105,8 @@ export default definePlugin({
|
||||
// Bots basically never send a nonce, and if someone does do it then it's usually not a snowflake
|
||||
if (message.author.bot) return null;
|
||||
|
||||
if (ignoreSelf && message.author.id === AuthenticationStore.getId()) return null;
|
||||
|
||||
let isDiscordKotlin = false;
|
||||
let delta = SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce); // milliseconds
|
||||
if (!showMillis) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user