Merge branch 'main' of https://github.com/Nexulien/Nexulien
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.12.13",
|
"version": "1.13.2",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
"bugs": {
|
"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 "./channel";
|
||||||
export * from "./commands";
|
export * from "./commands";
|
||||||
export * from "./messages";
|
export * from "./messages";
|
||||||
|
|||||||
@@ -11,3 +11,586 @@ export const enum StickerFormatType {
|
|||||||
LOTTIE = 3,
|
LOTTIE = 3,
|
||||||
GIF = 4
|
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",
|
"name": "@vencord/discord-types",
|
||||||
"author": "Vencord Contributors",
|
"author": "Vencord Contributors",
|
||||||
|
"private": false,
|
||||||
"description": "Typescript definitions for the webpack modules of the Discord Web app",
|
"description": "Typescript definitions for the webpack modules of the Discord Web app",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "LGPL-3.0-or-later",
|
"license": "LGPL-3.0-or-later",
|
||||||
@@ -12,8 +13,10 @@
|
|||||||
"directory": "packages/discord-types"
|
"directory": "packages/discord-types"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/react": "^19.0.10",
|
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.22.2",
|
||||||
"type-fest": "^4.41.0"
|
"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 "./Application";
|
||||||
export * from "./Channel";
|
export * from "./Channel";
|
||||||
export * from "./Guild";
|
export * from "./Guild";
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { CommandOption } from './Commands';
|
|||||||
import { User, UserJSON } from '../User';
|
import { User, UserJSON } from '../User';
|
||||||
import { Embed, EmbedJSON } from './Embed';
|
import { Embed, EmbedJSON } from './Embed';
|
||||||
import { DiscordRecord } from "../Record";
|
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;
|
* TODO: looks like discord has moved over to Date instead of Moment;
|
||||||
*/
|
*/
|
||||||
export class Message extends DiscordRecord {
|
export class Message extends DiscordRecord {
|
||||||
@@ -35,7 +35,7 @@ export class Message extends DiscordRecord {
|
|||||||
customRenderedContent: unknown;
|
customRenderedContent: unknown;
|
||||||
editedTimestamp: Date;
|
editedTimestamp: Date;
|
||||||
embeds: Embed[];
|
embeds: Embed[];
|
||||||
flags: number;
|
flags: MessageFlags;
|
||||||
giftCodes: string[];
|
giftCodes: string[];
|
||||||
id: string;
|
id: string;
|
||||||
interaction: {
|
interaction: {
|
||||||
@@ -100,7 +100,7 @@ export class Message extends DiscordRecord {
|
|||||||
stickers: unknown[];
|
stickers: unknown[];
|
||||||
timestamp: moment.Moment;
|
timestamp: moment.Moment;
|
||||||
tts: boolean;
|
tts: boolean;
|
||||||
type: number;
|
type: MessageType;
|
||||||
webhookId: string | undefined;
|
webhookId: string | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,10 +121,13 @@ export class Message extends DiscordRecord {
|
|||||||
removeReaction(emoji: ReactionEmoji, fromCurrentUser: boolean): Message;
|
removeReaction(emoji: ReactionEmoji, fromCurrentUser: boolean): Message;
|
||||||
|
|
||||||
getChannelId(): string;
|
getChannelId(): string;
|
||||||
hasFlag(flag: number): boolean;
|
hasFlag(flag: MessageFlags): boolean;
|
||||||
isCommandType(): boolean;
|
isCommandType(): boolean;
|
||||||
isEdited(): boolean;
|
isEdited(): boolean;
|
||||||
isSystemDM(): boolean;
|
isSystemDM(): boolean;
|
||||||
|
|
||||||
|
/** Vencord added */
|
||||||
|
deleted?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A smaller Message object found in FluxDispatcher and elsewhere. */
|
/** A smaller Message object found in FluxDispatcher and elsewhere. */
|
||||||
@@ -193,3 +196,9 @@ export interface MessageReaction {
|
|||||||
emoji: ReactionEmoji;
|
emoji: ReactionEmoji;
|
||||||
me: boolean;
|
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;
|
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 = {
|
export type CheckboxAligns = {
|
||||||
CENTER: "center";
|
CENTER: "center";
|
||||||
TOP: "top";
|
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 };
|
export { FluxEvents };
|
||||||
|
|
||||||
|
type FluxEventsAutoComplete = LiteralUnion<FluxEvents, string>;
|
||||||
|
|
||||||
export interface FluxDispatcher {
|
export interface FluxDispatcher {
|
||||||
_actionHandlers: any;
|
_actionHandlers: any;
|
||||||
_subscriptions: any;
|
_subscriptions: any;
|
||||||
dispatch(event: { [key: string]: unknown; type: FluxEvents; }): Promise<void>;
|
dispatch(event: { [key: string]: unknown; type: FluxEventsAutoComplete; }): Promise<void>;
|
||||||
isDispatching(): boolean;
|
isDispatching(): boolean;
|
||||||
subscribe(event: FluxEvents, callback: (data: any) => void): void;
|
subscribe(event: FluxEventsAutoComplete, callback: (data: any) => void): void;
|
||||||
unsubscribe(event: FluxEvents, callback: (data: any) => void): void;
|
unsubscribe(event: FluxEventsAutoComplete, callback: (data: any) => void): void;
|
||||||
wait(callback: () => void): void;
|
wait(callback: () => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@vencord/types",
|
"name": "@vencord/types",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.11.5",
|
"version": "1.13.2",
|
||||||
"description": "",
|
"description": "",
|
||||||
"types": "index.d.ts",
|
"types": "index.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -19,9 +19,14 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/lodash": "4.17.15",
|
"@types/lodash": "4.17.15",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.13.4",
|
||||||
"@types/react": "18.3.1",
|
"@vencord/discord-types": "^1.0.0",
|
||||||
"@types/react-dom": "18.3.1",
|
"highlight.js": "11.11.1",
|
||||||
"standalone-electron-types": "^34.2.0",
|
"moment": "^2.22.2",
|
||||||
|
"ts-pattern": "^5.6.0",
|
||||||
"type-fest": "^4.35.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':
|
'@types/react-dom':
|
||||||
specifier: 18.3.1
|
specifier: 18.3.1
|
||||||
version: 18.3.1
|
version: 18.3.1
|
||||||
standalone-electron-types:
|
'@vencord/discord-types':
|
||||||
specifier: ^34.2.0
|
specifier: ^1.0.0
|
||||||
version: 34.2.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:
|
type-fest:
|
||||||
specifier: ^4.35.0
|
specifier: ^4.35.0
|
||||||
version: 4.38.0
|
version: 4.38.0
|
||||||
@@ -742,6 +751,11 @@ packages:
|
|||||||
'@vap/shiki@0.10.5':
|
'@vap/shiki@0.10.5':
|
||||||
resolution: {integrity: sha512-5BHVGvQT8qonbLSASon5aQFQ18OZU4FxSl9tLSj6oJ0sap3KdMbYcfGq25M9zFZR1g1dJN7fgjmZXBIS5omIdw==}
|
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:
|
abort-controller@3.0.0:
|
||||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||||
engines: {node: '>=6.5'}
|
engines: {node: '>=6.5'}
|
||||||
@@ -3499,6 +3513,12 @@ snapshots:
|
|||||||
vscode-oniguruma: 1.7.0
|
vscode-oniguruma: 1.7.0
|
||||||
vscode-textmate: 5.2.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:
|
abort-controller@3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
event-target-shim: 5.0.1
|
event-target-shim: 5.0.1
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.nx-error-card {
|
.vc-error-card {
|
||||||
border: 1px solid var(--border-subtle);
|
border: 1px solid var(--border-subtle);
|
||||||
background-color: var(--background-feedback-critical);
|
background-color: var(--background-feedback-critical);
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import type { HTMLProps } from "react";
|
|||||||
|
|
||||||
export function ErrorCard(props: React.PropsWithChildren<HTMLProps<HTMLDivElement>>) {
|
export function ErrorCard(props: React.PropsWithChildren<HTMLProps<HTMLDivElement>>) {
|
||||||
return (
|
return (
|
||||||
<div {...props} className={classes(props.className, "nx-error-card")}>
|
<div {...props} className={classes(props.className, "vc-error-card")}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</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>) {
|
function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren<BaseIconProps>) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
className={classes(className, "nx-icon")}
|
className={classes(className, "vc-icon")}
|
||||||
role="img"
|
role="img"
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
@@ -51,7 +51,7 @@ export function LinkIcon({ height = 24, width = 24, className }: IconProps) {
|
|||||||
<Icon
|
<Icon
|
||||||
height={height}
|
height={height}
|
||||||
width={width}
|
width={width}
|
||||||
className={classes(className, "nx-link-icon")}
|
className={classes(className, "vc-link-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<g fill="none" fillRule="evenodd">
|
<g fill="none" fillRule="evenodd">
|
||||||
@@ -69,7 +69,7 @@ export function CopyIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-copy-icon")}
|
className={classes(props.className, "vc-copy-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<g fill="currentColor">
|
<g fill="currentColor">
|
||||||
@@ -88,7 +88,7 @@ export function OpenExternalIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-open-external-icon")}
|
className={classes(props.className, "vc-open-external-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<polygon
|
<polygon
|
||||||
@@ -104,7 +104,7 @@ export function ImageIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-image-icon")}
|
className={classes(props.className, "vc-image-icon")}
|
||||||
viewBox="0 0 24 24"
|
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" />
|
<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 (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-info-icon")}
|
className={classes(props.className, "vc-info-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -133,7 +133,7 @@ export function OwnerCrownIcon(props: IconProps) {
|
|||||||
<Icon
|
<Icon
|
||||||
aria-label={getIntlMessage("GUILD_OWNER")}
|
aria-label={getIntlMessage("GUILD_OWNER")}
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-owner-crown-icon")}
|
className={classes(props.className, "vc-owner-crown-icon")}
|
||||||
role="img"
|
role="img"
|
||||||
viewBox="0 0 16 16"
|
viewBox="0 0 16 16"
|
||||||
>
|
>
|
||||||
@@ -154,7 +154,7 @@ export function ScreenshareIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-screenshare-icon")}
|
className={classes(props.className, "vc-screenshare-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -169,7 +169,7 @@ export function ImageVisible(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-image-visible")}
|
className={classes(props.className, "vc-image-visible")}
|
||||||
viewBox="0 0 24 24"
|
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" />
|
<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 (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-image-invisible")}
|
className={classes(props.className, "vc-image-invisible")}
|
||||||
viewBox="0 0 24 24"
|
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" />
|
<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 (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-microphone")}
|
className={classes(props.className, "vc-microphone")}
|
||||||
viewBox="0 0 24 24"
|
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" />
|
<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 (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-cog-wheel")}
|
className={classes(props.className, "vc-cog-wheel")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -223,7 +223,7 @@ export function ReplyIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-reply-icon")}
|
className={classes(props.className, "vc-reply-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -238,7 +238,7 @@ export function DeleteIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-delete-icon")}
|
className={classes(props.className, "vc-delete-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -257,7 +257,7 @@ export function PlusIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-plus-icon")}
|
className={classes(props.className, "vc-plus-icon")}
|
||||||
viewBox="0 0 18 18"
|
viewBox="0 0 18 18"
|
||||||
>
|
>
|
||||||
<polygon
|
<polygon
|
||||||
@@ -273,7 +273,7 @@ export function NoEntrySignIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-no-entry-sign-icon")}
|
className={classes(props.className, "vc-no-entry-sign-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -292,7 +292,7 @@ export function SafetyIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-safety-icon")}
|
className={classes(props.className, "vc-safety-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -310,7 +310,7 @@ export function NotesIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-notes-icon")}
|
className={classes(props.className, "vc-notes-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -331,7 +331,7 @@ export function FolderIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-folder-icon")}
|
className={classes(props.className, "vc-folder-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -346,7 +346,7 @@ export function LogIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-log-icon")}
|
className={classes(props.className, "vc-log-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -363,7 +363,7 @@ export function RestartIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-restart-icon")}
|
className={classes(props.className, "vc-restart-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -378,7 +378,7 @@ export function PaintbrushIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-paintbrush-icon")}
|
className={classes(props.className, "vc-paintbrush-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
@@ -395,7 +395,7 @@ export function PencilIcon(props: IconProps) {
|
|||||||
return (
|
return (
|
||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "nx-pencil-icon")}
|
className={classes(props.className, "vc-pencil-icon")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<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);
|
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 {
|
.nx-card-warning {
|
||||||
background-color: var(--background-feedback-warning) !important;
|
background-color: var(--background-feedback-warning) !important;
|
||||||
}
|
}
|
||||||
@@ -28,7 +37,7 @@
|
|||||||
background-color: unset !important;
|
background-color: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-card-title {
|
.nx-title {
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -39,3 +48,11 @@
|
|||||||
line-height: 2;
|
line-height: 2;
|
||||||
margin-top: -8px;
|
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 { default as ErrorBoundary } from "./ErrorBoundary";
|
||||||
export * from "./ErrorCard";
|
export * from "./ErrorCard";
|
||||||
export * from "./Flex";
|
export * from "./Flex";
|
||||||
|
export * from "./FormDivider";
|
||||||
|
export * from "./FormSwitch";
|
||||||
export * from "./Grid";
|
export * from "./Grid";
|
||||||
export * from "./Heart";
|
export * from "./Heart";
|
||||||
export * from "./Icons";
|
export * from "./Icons";
|
||||||
export * from "./Link";
|
export * from "./Link";
|
||||||
export * from "./settings";
|
export * from "./settings";
|
||||||
|
export * from "./Switch";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.nx-addon-card {
|
.vc-addon-card {
|
||||||
background-color: var(--background-secondary-alt);
|
background-color: var(--background-secondary-alt);
|
||||||
color: var(--interactive-active);
|
color: var(--interactive-active);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -12,27 +12,27 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.visual-refresh .nx-addon-card {
|
.visual-refresh .vc-addon-card {
|
||||||
background-color: var(--card-primary-bg);
|
background-color: var(--card-primary-bg);
|
||||||
border: 1px solid var(--border-subtle);
|
border: 1px solid var(--border-subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-card-disabled {
|
.vc-addon-card-disabled {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-card:hover {
|
.vc-addon-card:hover {
|
||||||
background-color: var(--background-tertiary);
|
background-color: var(--background-tertiary);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: var(--elevation-high);
|
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 */
|
/* same as non-hover, here to overwrite the non-refresh hover background */
|
||||||
background-color: var(--card-primary-bg);
|
background-color: var(--card-primary-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-header {
|
.vc-addon-header {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-note {
|
.vc-addon-note {
|
||||||
height: 36px;
|
height: 36px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@@ -54,11 +54,11 @@
|
|||||||
box-orient: vertical;
|
box-orient: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-name-author {
|
.vc-addon-name-author {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-name {
|
.vc-addon-name {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -66,22 +66,22 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-author {
|
.vc-addon-author {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-author::before {
|
.vc-addon-author::before {
|
||||||
content: "by ";
|
content: "by ";
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-title-container {
|
.vc-addon-title-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 1.25em;
|
height: 1.25em;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-title {
|
.vc-addon-title {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-title:hover {
|
.vc-addon-title:hover {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
animation: vc-addon-title var(--duration) linear infinite;
|
animation: vc-addon-title var(--duration) linear infinite;
|
||||||
}
|
}
|
||||||
@@ -20,11 +20,11 @@ import "./AddonCard.css";
|
|||||||
|
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { AddonBadge } from "@components/settings/PluginBadge";
|
import { AddonBadge } from "@components/settings/PluginBadge";
|
||||||
import { Switch } from "@components/settings/Switch";
|
import { Switch } from "@components/Switch";
|
||||||
import { Text, useRef } from "@webpack/common";
|
import { Text, useRef } from "@webpack/common";
|
||||||
import type { MouseEventHandler, ReactNode } from "react";
|
import type { MouseEventHandler, ReactNode } from "react";
|
||||||
|
|
||||||
const cl = classNameFactory("nx-addon-");
|
const cl = classNameFactory("vc-addon-");
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
name: ReactNode;
|
name: ReactNode;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
export function AddonBadge({ text, color }) {
|
export function AddonBadge({ text, color }) {
|
||||||
return (
|
return (
|
||||||
<div className="nx-addon-badge" style={{
|
<div className="vc-addon-badge" style={{
|
||||||
backgroundColor: color,
|
backgroundColor: color,
|
||||||
justifySelf: "flex-end",
|
justifySelf: "flex-end",
|
||||||
marginLeft: "auto"
|
marginLeft: "auto"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.nx-settings-quickActions-container {
|
.vc-settings-quickActions-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
grid-template-rows: auto auto;
|
grid-template-rows: auto auto;
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-quickActions-title {
|
.vc-settings-quickActions-title {
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -14,36 +14,36 @@
|
|||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-quickActions-containerButtons-2 {
|
.vc-settings-quickActions-containerButtons-2 {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-quickActions-containerButtons-3 {
|
.vc-settings-quickActions-containerButtons-3 {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-quickActions-button {
|
.vc-settings-quickActions-button {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-quickActions-button:focus-visible {
|
.vc-settings-quickActions-button:focus-visible {
|
||||||
outline: 2px solid var(--focus-primary);
|
outline: 2px solid var(--focus-primary);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
transition-duration: 0s;
|
transition-duration: 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-quickActions-img {
|
.vc-settings-quickActions-img {
|
||||||
width: 24px !important;
|
width: 24px !important;
|
||||||
height: 24px !important;
|
height: 24px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-quickActions-info-button {
|
.vc-settings-quickActions-info-button {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -54,17 +54,17 @@
|
|||||||
margin-top: -2px !important;
|
margin-top: -2px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-quickActions-info-button:hover {
|
.vc-settings-quickActions-info-button:hover {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-quickActions-info-button:focus-visible {
|
.vc-settings-quickActions-info-button:focus-visible {
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
outline: 2px solid var(--focus-primary);
|
outline: 2px solid var(--focus-primary);
|
||||||
outline-offset: -2px;
|
outline-offset: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-quickActions-help {
|
.vc-settings-quickActions-help {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import "./QuickAction.css";
|
|||||||
|
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { InfoIcon } from "@components/Icons";
|
import { InfoIcon } from "@components/Icons";
|
||||||
import { NxCard, NxCardTitle } from "@components/NxCard";
|
import { NxCard, NxText, NxTitle } from "@components/NxComponents";
|
||||||
import { openInviteModal } from "@utils/discord";
|
import { openInviteModal } from "@utils/discord";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { closeAllModals } from "@utils/modal";
|
import { closeAllModals } from "@utils/modal";
|
||||||
@@ -16,7 +16,7 @@ import { findByPropsLazy } from "@webpack";
|
|||||||
import { Alerts, Button, FluxDispatcher, GuildStore, NavigationRouter } from "@webpack/common";
|
import { Alerts, Button, FluxDispatcher, GuildStore, NavigationRouter } from "@webpack/common";
|
||||||
import type { ComponentType, PropsWithChildren, ReactNode } from "react";
|
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");
|
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
|
||||||
|
|
||||||
export interface QuickActionProps {
|
export interface QuickActionProps {
|
||||||
@@ -46,7 +46,7 @@ export function QuickAction(props: QuickActionProps) {
|
|||||||
export function QuickActionContainer({ title, children, columns = "3" }: PropsWithChildren<QuickActionContainerProps>) {
|
export function QuickActionContainer({ title, children, columns = "3" }: PropsWithChildren<QuickActionContainerProps>) {
|
||||||
return (
|
return (
|
||||||
<NxCard className={cl("container")}>
|
<NxCard className={cl("container")}>
|
||||||
<NxCardTitle className={cl("title")}>
|
<NxTitle className={cl("title")}>
|
||||||
{title}
|
{title}
|
||||||
<button
|
<button
|
||||||
role="switch"
|
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>
|
<img height="64px" width="64px" src="https://cdn.discordapp.com/emojis/1348781960453161011.gif" draggable="false"></img>
|
||||||
<p>No one's around to help.</p>
|
<p>No one's around to help.</p>
|
||||||
</div>
|
</div>
|
||||||
<NxCard className="nx-card-help">
|
<NxCard variant="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.
|
<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>
|
</NxCard>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
@@ -84,7 +84,7 @@ export function QuickActionContainer({ title, children, columns = "3" }: PropsWi
|
|||||||
>
|
>
|
||||||
<InfoIcon />
|
<InfoIcon />
|
||||||
</button>
|
</button>
|
||||||
</NxCardTitle>
|
</NxTitle>
|
||||||
<span className={cl("containerButtons-" + columns)}>{children}</span>
|
<span className={cl("containerButtons-" + columns)}>{children}</span>
|
||||||
</NxCard>
|
</NxCard>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
.nx-donate-button {
|
.vc-donate-button {
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-donate-button .vc-heart-icon {
|
.vc-donate-button .vc-heart-icon {
|
||||||
transition: transform 0.3s;
|
transition: transform 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-donate-button:hover .vc-heart-icon {
|
.vc-donate-button:hover .vc-heart-icon {
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-special-card-special {
|
.vc-special-card-special {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
background-size: cover !important;
|
background-size: cover !important;
|
||||||
@@ -21,12 +21,12 @@
|
|||||||
background-origin: border-box !important;
|
background-origin: border-box !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-special-card-flex {
|
.vc-special-card-flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-special-card-flex-main {
|
.vc-special-card-flex-main {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -35,25 +35,25 @@
|
|||||||
text-shadow: 2px 2px 2px #70e4;
|
text-shadow: 2px 2px 2px #70e4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-special-title {
|
.vc-special-title {
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-special-text {
|
.vc-special-text {
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-special-seperator {
|
.vc-special-seperator {
|
||||||
margin-top: 0.75em;
|
margin-top: 0.75em;
|
||||||
border-top: 1px solid white;
|
border-top: 1px solid white;
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-special-hyperlink > .nx-special-hyperlink-text {
|
.vc-special-hyperlink > .vc-special-hyperlink-text {
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-special-hyperlink {
|
.vc-special-hyperlink {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-special-image-container {
|
.vc-special-image-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -80,6 +80,6 @@
|
|||||||
height: 70px;
|
height: 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-special-image {
|
.vc-special-image {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ import { classNameFactory } from "@api/Styles";
|
|||||||
import { Card, Forms, React } from "@webpack/common";
|
import { Card, Forms, React } from "@webpack/common";
|
||||||
import type { PropsWithChildren } from "react";
|
import type { PropsWithChildren } from "react";
|
||||||
|
|
||||||
const cl = classNameFactory("nx-special-");
|
const cl = classNameFactory("vc-special-");
|
||||||
|
|
||||||
interface StyledCardProps {
|
interface StyledCardProps {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-header {
|
.vc-settings-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 128px;
|
gap: 128px;
|
||||||
@@ -20,13 +20,13 @@
|
|||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-header-minimal > div > .nx-settings-logo-container {
|
.vc-settings-header-minimal > div > .vc-settings-logo-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 20%;
|
height: 20%;
|
||||||
min-height: 36px;
|
min-height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes nx-settings-logo-boioioing {
|
@keyframes vc-settings-logo-boioioing {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(1) scaleX(1) scaleY(1);
|
transform: scale(1) scaleX(1) scaleY(1);
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-logo {
|
.vc-settings-logo {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
@@ -56,33 +56,33 @@
|
|||||||
|
|
||||||
.nx-mascot {
|
.nx-mascot {
|
||||||
/* When adjusting make sure image reaches out of card top for ~16px */
|
/* 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;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-header-minimal > div {
|
.vc-settings-header-minimal > div {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
min-width: 100% !important;
|
min-width: 100% !important;
|
||||||
max-height: 30px;
|
max-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-header-minimal .nx-settings-logo {
|
.vc-settings-header-minimal .vc-settings-logo {
|
||||||
transform: translateY(-4px);
|
transform: translateY(-4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-buttonRow {
|
.vc-settings-buttonRow {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-buttonRow-minimal {
|
.vc-settings-buttonRow-minimal {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
min-width: fit-content !important;
|
min-width: fit-content !important;
|
||||||
max-height: fit-content;
|
max-height: fit-content;
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export * from "../Switch";
|
||||||
export * from "./AddonCard";
|
export * from "./AddonCard";
|
||||||
export * from "./DonateButton";
|
export * from "./DonateButton";
|
||||||
export * from "./PluginBadge";
|
export * from "./PluginBadge";
|
||||||
export * from "./QuickAction";
|
export * from "./QuickAction";
|
||||||
export * from "./SpecialCard";
|
export * from "./SpecialCard";
|
||||||
export * from "./Switch";
|
|
||||||
export * from "./tabs";
|
export * from "./tabs";
|
||||||
|
|||||||
@@ -1,10 +1,30 @@
|
|||||||
:root {
|
:root {
|
||||||
--nx-green: #0f9;
|
--nx-green: #0f9;
|
||||||
--nx-purple: #70e;
|
--nx-purple: #70e;
|
||||||
|
--nx-vc-pink: #d3869b;
|
||||||
--nx-green-background: #00ff9914;
|
--nx-green-background: #00ff9914;
|
||||||
--nx-purple-background: #7700ee14;
|
--nx-purple-background: #7700ee14;
|
||||||
|
--nx-vc-pink-background: #d3869b14;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-removeSwitchDivider div[class*="divider"] {
|
.nx-code {
|
||||||
display: none;
|
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; }>) {
|
export function SettingsTab({ title, children }: PropsWithChildren<{ title: string; }>) {
|
||||||
return (
|
return (
|
||||||
<Forms.FormSection>
|
<section>
|
||||||
<Text
|
<Text
|
||||||
variant="heading-lg/semibold"
|
variant="heading-lg/semibold"
|
||||||
tag="h2"
|
tag="h2"
|
||||||
@@ -35,7 +35,7 @@ export function SettingsTab({ title, children }: PropsWithChildren<{ title: stri
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
</Forms.FormSection>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,18 +67,18 @@ function NotificationsTab() {
|
|||||||
<TabBar
|
<TabBar
|
||||||
type="top"
|
type="top"
|
||||||
look="brand"
|
look="brand"
|
||||||
className={classes("nx-settings-tab-bar", Margins.top16)}
|
className={classes("nx-notifications-tab-bar", Margins.top16)}
|
||||||
selectedItem={currentTab}
|
selectedItem={currentTab}
|
||||||
onItemSelect={setCurrentTab}
|
onItemSelect={setCurrentTab}
|
||||||
>
|
>
|
||||||
<TabBar.Item
|
<TabBar.Item
|
||||||
className="nx-settings-tab-bar-item"
|
className="nx-notifications-tab-bar-item"
|
||||||
id={NotificationTab.LOG}
|
id={NotificationTab.LOG}
|
||||||
>
|
>
|
||||||
Notification Log
|
Notification Log
|
||||||
</TabBar.Item>
|
</TabBar.Item>
|
||||||
<TabBar.Item
|
<TabBar.Item
|
||||||
className="nx-settings-tab-bar-item"
|
className="nx-notifications-tab-bar-item"
|
||||||
id={NotificationTab.SETTINGS}
|
id={NotificationTab.SETTINGS}
|
||||||
>
|
>
|
||||||
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.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);
|
setFind(find instanceof RegExp ? `/${find.source}/` : find);
|
||||||
setParsedFind(find);
|
setParsedFind(find);
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { FormSwitch } from "@components/FormSwitch";
|
||||||
import { Margins } from "@utils/margins";
|
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 = {
|
const RegexGuide = {
|
||||||
"\\i": "Special regex escape sequence that matches identifiers (varnames, classnames, etc.)",
|
"\\i": "Special regex escape sequence that matches identifiers (varnames, classnames, etc.)",
|
||||||
@@ -69,15 +70,14 @@ export function ReplacementInput({ replacement, setReplacement, replacementError
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Switch
|
<FormSwitch
|
||||||
className={Margins.top16}
|
className={Margins.top16}
|
||||||
value={isFunc}
|
value={isFunc}
|
||||||
onChange={setIsFunc}
|
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
|
hideBorder
|
||||||
>
|
/>
|
||||||
Treat as Function
|
|
||||||
</Switch>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
.nx-author-modal-root {
|
.vc-author-modal-root {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-author-modal-header {
|
.vc-author-modal-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-author-modal-name {
|
.vc-author-modal-name {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
background: var(--background-base-lowest);
|
background: var(--background-base-lowest);
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
text-wrap: nowrap;
|
text-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-author-modal-name::before {
|
.vc-author-modal-name::before {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -34,19 +34,19 @@
|
|||||||
border-bottom-left-radius: 9999px;
|
border-bottom-left-radius: 9999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-author-modal-avatar {
|
.vc-author-modal-avatar {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-author-modal-links {
|
.vc-author-modal-links {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-author-modal-plugins {
|
.vc-author-modal-plugins {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
margin-top: 0.75em;
|
margin-top: 0.75em;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import Plugins from "~plugins";
|
|||||||
import { GithubButton, WebsiteButton } from "./LinkIconButton";
|
import { GithubButton, WebsiteButton } from "./LinkIconButton";
|
||||||
import { PluginCard } from "./PluginCard";
|
import { PluginCard } from "./PluginCard";
|
||||||
|
|
||||||
const cl = classNameFactory("nx-author-modal-");
|
const cl = classNameFactory("vc-author-modal-");
|
||||||
|
|
||||||
export function openContributorModal(user: User) {
|
export function openContributorModal(user: User) {
|
||||||
openModal(modalProps =>
|
openModal(modalProps =>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.nx-settings-modal-link-icon {
|
.vc-settings-modal-link-icon {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
box-sizing: border-box
|
box-sizing: border-box
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-modal-links {
|
.vc-settings-modal-links {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.2em;
|
gap: 0.2em;
|
||||||
}
|
}
|
||||||
@@ -12,12 +12,12 @@ import { MaskedLink, Tooltip } from "@webpack/common";
|
|||||||
|
|
||||||
export function GithubLinkIcon() {
|
export function GithubLinkIcon() {
|
||||||
const theme = getTheme() === Theme.Light ? "#000000" : "#FFFFFF";
|
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() {
|
export function WebsiteLinkIcon() {
|
||||||
const theme = getTheme() === Theme.Light ? "#000000" : "#FFFFFF";
|
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 {
|
interface Props {
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
.nx-plugin-modal-title {
|
.vc-plugin-modal-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugin-modal-title div {
|
.vc-plugin-modal-title div {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
transform: translateY(1px);
|
transform: translateY(1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugin-modal-info {
|
.vc-plugin-modal-info {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugin-modal-description {
|
.vc-plugin-modal-description {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ import { useSettings } from "@api/Settings";
|
|||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { NxCard } from "@components/NxCard";
|
import { NxCard, NxText } from "@components/NxComponents";
|
||||||
import { debounce } from "@shared/debounce";
|
import { debounce } from "@shared/debounce";
|
||||||
import { proxyLazy } from "@utils/lazy";
|
import { proxyLazy } from "@utils/lazy";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
@@ -40,7 +40,7 @@ import { PluginMeta } from "~plugins";
|
|||||||
import { OptionComponentMap } from "./components";
|
import { OptionComponentMap } from "./components";
|
||||||
import { openContributorModal } from "./ContributorModal";
|
import { openContributorModal } from "./ContributorModal";
|
||||||
|
|
||||||
const cl = classNameFactory("nx-plugin-modal-");
|
const cl = classNameFactory("vc-plugin-modal-");
|
||||||
|
|
||||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||||
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
|
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() {
|
function renderSettings() {
|
||||||
if (!hasSettings || !plugin.options)
|
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]) => {
|
const options = Object.entries(plugin.options).map(([key, setting]) => {
|
||||||
if (setting.type === OptionType.CUSTOM || setting.hidden) return null;
|
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];
|
const Component = OptionComponentMap[setting.type];
|
||||||
return (
|
return (
|
||||||
|
<ErrorBoundary noop key={key}>
|
||||||
<Component
|
<Component
|
||||||
id={key}
|
id={key}
|
||||||
key={key}
|
|
||||||
option={setting}
|
option={setting}
|
||||||
onChange={debounce(onChange)}
|
onChange={debounce(onChange)}
|
||||||
pluginSettings={pluginSettings}
|
pluginSettings={pluginSettings}
|
||||||
definedSettings={plugin.settings}
|
definedSettings={plugin.settings}
|
||||||
/>
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NxCard className={Margins.top16}>
|
<NxCard variant="grand" className={classes(Margins.top16, Margins.bottom8)}>
|
||||||
<div className="nx-plugins-settings">
|
<div className="vc-plugins-settings">
|
||||||
{options}
|
{options}
|
||||||
</div>
|
</div>
|
||||||
</NxCard>
|
</NxCard>
|
||||||
@@ -177,25 +180,25 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalContent className={Margins.bottom16}>
|
<ModalContent className={Margins.bottom16}>
|
||||||
<Forms.FormSection>
|
<section>
|
||||||
<Flex className={cl("info")}>
|
<Flex className={cl("info")}>
|
||||||
<Forms.FormText className={cl("description")}>{plugin.description}</Forms.FormText>
|
<Forms.FormText className={cl("description")}>{plugin.description}</Forms.FormText>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Forms.FormSection>
|
</section>
|
||||||
|
|
||||||
{!!plugin.settingsAboutComponent && (
|
{!!plugin.settingsAboutComponent && (
|
||||||
<div className={Margins.top16}>
|
<div className={Margins.top16}>
|
||||||
<Forms.FormSection>
|
<section>
|
||||||
<ErrorBoundary message="An error occurred while rendering this plugin's custom Info Component">
|
<ErrorBoundary message="An error occurred while rendering this plugin's custom Info Component">
|
||||||
<plugin.settingsAboutComponent />
|
<plugin.settingsAboutComponent />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</Forms.FormSection>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Forms.FormSection>
|
<section>
|
||||||
{renderSettings()}
|
{renderSettings()}
|
||||||
</Forms.FormSection>
|
</section>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</ModalRoot>
|
</ModalRoot>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* 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 { PluginOptionBoolean } from "@utils/types";
|
||||||
import { React, useState } from "@webpack/common";
|
import { React, useState } from "@webpack/common";
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { DefinedSettings, PluginOptionBase } from "@utils/types";
|
|||||||
import { Text } from "@webpack/common";
|
import { Text } from "@webpack/common";
|
||||||
import { PropsWithChildren } from "react";
|
import { PropsWithChildren } from "react";
|
||||||
|
|
||||||
export const cl = classNameFactory("nx-plugins-setting-");
|
export const cl = classNameFactory("vc-plugins-setting-");
|
||||||
|
|
||||||
interface SettingBaseProps<T> {
|
interface SettingBaseProps<T> {
|
||||||
option: T;
|
option: T;
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
.nx-plugins-setting-section {
|
.vc-plugins-setting-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-setting-content {
|
.vc-plugins-setting-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-setting-inline {
|
.vc-plugins-setting-inline {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-setting-label {
|
.vc-plugins-setting-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.25em;
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-setting-title {
|
.vc-plugins-setting-title {
|
||||||
color: var(--header-primary);
|
color: var(--header-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-setting-description {
|
.vc-plugins-setting-description {
|
||||||
color: var(--header-secondary);
|
color: var(--header-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-setting-error {
|
.vc-plugins-setting-error {
|
||||||
color: var(--text-danger);
|
color: var(--text-danger);
|
||||||
}
|
}
|
||||||
@@ -21,8 +21,9 @@ import "./styles.css";
|
|||||||
import * as DataStore from "@api/DataStore";
|
import * as DataStore from "@api/DataStore";
|
||||||
import { useSettings } from "@api/Settings";
|
import { useSettings } from "@api/Settings";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Flex } from "@components/Flex";
|
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 { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||||
import { ChangeList } from "@utils/ChangeList";
|
import { ChangeList } from "@utils/ChangeList";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
@@ -37,7 +38,7 @@ import Plugins, { ExcludedPlugins } from "~plugins";
|
|||||||
|
|
||||||
import { PluginCard } from "./PluginCard";
|
import { PluginCard } from "./PluginCard";
|
||||||
|
|
||||||
export const cl = classNameFactory("nx-plugins-");
|
export const cl = classNameFactory("vc-plugins-");
|
||||||
export const logger = new Logger("PluginSettings", "#a6d189");
|
export const logger = new Logger("PluginSettings", "#a6d189");
|
||||||
|
|
||||||
const InputStyles = findByPropsLazy("inputWrapper", "inputError", "error");
|
const InputStyles = findByPropsLazy("inputWrapper", "inputError", "error");
|
||||||
@@ -46,16 +47,18 @@ function ReloadRequiredCard({ required }: { required: boolean; }) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NxCard className={cl("info-card")}>
|
<NxCard className={cl("info-card")}>
|
||||||
<NxCardTitle>Plugin Management</NxCardTitle>
|
<NxTitle>Plugin Management</NxTitle>
|
||||||
<span>Press the cog wheel or info icon to get more info on a plugin</span>
|
<NxText>
|
||||||
<span> — Plugins with a cog wheel have settings you can modify!</span>
|
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>
|
</NxCard>
|
||||||
{required ? (
|
{required ? (
|
||||||
<NxCard className={classes("nx-card-warning", Margins.bottom16)}>
|
<NxCard variant="warning" className={Margins.bottom16}>
|
||||||
<Flex flexDirection="row" style={{ justifyContent: "space-between" }}>
|
<Flex flexDirection="row" style={{ justifyContent: "space-between" }}>
|
||||||
<div>
|
<div>
|
||||||
<NxCardTitle>Restart required!</NxCardTitle>
|
<NxTitle>Restart required!</NxTitle>
|
||||||
<span>Restart now to apply new plugins and their settings</span>
|
<NxText>Restart now to apply new plugins and their settings</NxText>
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={() => location.reload()} className={cl("restart-button")}>
|
<Button onClick={() => location.reload()} className={cl("restart-button")}>
|
||||||
Restart
|
Restart
|
||||||
@@ -258,8 +261,11 @@ function PluginSettings() {
|
|||||||
</Forms.FormTitle> */}
|
</Forms.FormTitle> */}
|
||||||
|
|
||||||
<div className={classes(Margins.bottom20, cl("filter-controls"))}>
|
<div className={classes(Margins.bottom20, cl("filter-controls"))}>
|
||||||
|
<ErrorBoundary noop>
|
||||||
<TextInput autoFocus value={searchValue.value} placeholder="Search for a plugin..." onChange={onSearch} />
|
<TextInput autoFocus value={searchValue.value} placeholder="Search for a plugin..." onChange={onSearch} />
|
||||||
|
</ErrorBoundary>
|
||||||
<div className={InputStyles.inputWrapper}>
|
<div className={InputStyles.inputWrapper}>
|
||||||
|
<ErrorBoundary noop>
|
||||||
<Select
|
<Select
|
||||||
options={[
|
options={[
|
||||||
{ label: "Show All", value: SearchStatus.ALL, default: true },
|
{ label: "Show All", value: SearchStatus.ALL, default: true },
|
||||||
@@ -274,6 +280,7 @@ function PluginSettings() {
|
|||||||
isSelected={v => v === searchValue.status}
|
isSelected={v => v === searchValue.status}
|
||||||
closeOnSelect={true}
|
closeOnSelect={true}
|
||||||
/>
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,14 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.nx-plugins-grid {
|
.vc-plugins-grid {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: 16px;
|
grid-gap: 16px;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-info-button {
|
.vc-plugins-info-button {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -31,18 +31,18 @@
|
|||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-settings-button:hover {
|
.vc-plugins-settings-button:hover {
|
||||||
color: var(--interactive-hover);
|
color: var(--interactive-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-filter-controls {
|
.vc-plugins-filter-controls {
|
||||||
display: grid;
|
display: grid;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
grid-template-columns: 1fr 200px;
|
grid-template-columns: 1fr 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-addon-badge {
|
.vc-addon-badge {
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -54,23 +54,23 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-dep-name {
|
.vc-plugins-dep-name {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-info-card {
|
.vc-plugins-info-card {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-restart-button {
|
.vc-plugins-restart-button {
|
||||||
background-color: var(--yellow-300) !important;
|
background-color: var(--yellow-300) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-info-icon:not(:hover, :focus) {
|
.vc-plugins-info-icon:not(:hover, :focus) {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-noResults {
|
.vc-plugins-noResults {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-plugins-settings {
|
.vc-plugins-settings {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1.25em;
|
gap: 1.25em;
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
.nx-settings-tab-bar {
|
.vc-settings-tab-bar {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
border-bottom: 1px solid var(--border-subtle);
|
border-bottom: 1px solid var(--border-subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-tab-bar-item {
|
.vc-settings-tab-bar-item {
|
||||||
margin-right: 32px;
|
margin-right: 32px;
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
margin-bottom: -2px;
|
margin-bottom: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-card {
|
.vc-settings-card {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-backup-restore-card {
|
.vc-backup-restore-card {
|
||||||
background-color: var(--info-warning-background);
|
background-color: var(--info-warning-background);
|
||||||
border-color: var(--info-warning-foreground);
|
border-color: var(--info-warning-foreground);
|
||||||
color: var(--info-warning-text);
|
color: var(--info-warning-text);
|
||||||
margin-bottom: 16px;
|
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 */
|
/* Needed to fix bad themes that hide certain textarea elements for whatever eldritch reason */
|
||||||
display: inline-block !important;
|
display: inline-block !important;
|
||||||
color: var(--text-default) !important;
|
color: var(--text-default) !important;
|
||||||
@@ -38,27 +38,27 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-theme-links::placeholder {
|
.vc-settings-theme-links::placeholder {
|
||||||
color: var(--text-muted) !important;
|
color: var(--text-muted) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-theme-links:focus {
|
.vc-settings-theme-links:focus {
|
||||||
background-color: var(--background-tertiary);
|
background-color: var(--background-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-cloud-settings-sync-grid {
|
.vc-cloud-settings-sync-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
grid-gap: 1em;
|
grid-gap: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-cloud-erase-data-danger-btn {
|
.vc-cloud-erase-data-danger-btn {
|
||||||
color: var(--white-500);
|
color: var(--white-500);
|
||||||
background-color: var(--button-danger-background);
|
background-color: var(--button-danger-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-text-selectable,
|
.vc-text-selectable,
|
||||||
.nx-text-selectable :where([class*="text" i], [class*="title" i]) {
|
.vc-text-selectable :where([class*="text" i], [class*="title" i]) {
|
||||||
/* make text selectable, silly discord makes the entirety of settings not selectable */
|
/* make text selectable, silly discord makes the entirety of settings not selectable */
|
||||||
user-select: text;
|
user-select: text;
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
import "@components/settings/tabs/styles.css";
|
import "@components/settings/tabs/styles.css";
|
||||||
|
|
||||||
import { Flex } from "@components/Flex";
|
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 { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { downloadSettingsBackup, uploadSettingsBackup } from "@utils/settingsSync";
|
import { downloadSettingsBackup, uploadSettingsBackup } from "@utils/settingsSync";
|
||||||
@@ -28,8 +28,8 @@ import { Button, Text } from "@webpack/common";
|
|||||||
export function BackupAndRestoreTab() {
|
export function BackupAndRestoreTab() {
|
||||||
return (
|
return (
|
||||||
<SettingsTab title="Backup & Restore">
|
<SettingsTab title="Backup & Restore">
|
||||||
<NxCard className={`nx-card-warning ${Margins.bottom16}`}>
|
<NxCard variant="warning" className={Margins.bottom16}>
|
||||||
<span>Importing a settings file will overwrite your current settings.</span>
|
<NxText>Importing a settings file will overwrite your current settings.</NxText>
|
||||||
</NxCard>
|
</NxCard>
|
||||||
<Text variant="text-md/normal" className={Margins.bottom8}>
|
<Text variant="text-md/normal" className={Margins.bottom8}>
|
||||||
You can import and export your Nexulien settings as a JSON file.
|
You can import and export your Nexulien settings as a JSON file.
|
||||||
|
|||||||
@@ -19,14 +19,15 @@
|
|||||||
import { showNotification } from "@api/Notifications";
|
import { showNotification } from "@api/Notifications";
|
||||||
import { Settings, useSettings } from "@api/Settings";
|
import { Settings, useSettings } from "@api/Settings";
|
||||||
import { CheckedTextInput } from "@components/CheckedTextInput";
|
import { CheckedTextInput } from "@components/CheckedTextInput";
|
||||||
|
import { FormSwitch } from "@components/FormSwitch";
|
||||||
import { Grid } from "@components/Grid";
|
import { Grid } from "@components/Grid";
|
||||||
import { Link } from "@components/Link";
|
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 { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||||
import { authorizeCloud, checkCloudUrlCsp, cloudLogger, deauthorizeCloud, getCloudAuth, getCloudUrl } from "@utils/cloud";
|
import { authorizeCloud, checkCloudUrlCsp, cloudLogger, deauthorizeCloud, getCloudAuth, getCloudUrl } from "@utils/cloud";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { deleteCloudSettings, getCloudSettings, putCloudSettings } from "@utils/settingsSync";
|
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) {
|
function validateUrl(url: string) {
|
||||||
try {
|
try {
|
||||||
@@ -70,22 +71,23 @@ function SettingsSyncSection() {
|
|||||||
const sectionEnabled = cloud.authenticated && cloud.settingsSync;
|
const sectionEnabled = cloud.authenticated && cloud.settingsSync;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NxCard className={Margins.top16}>
|
<section className={Margins.top16}>
|
||||||
<NxCardTitle>Settings Sync</NxCardTitle>
|
<NxCard>
|
||||||
|
<NxTitle>Settings Sync</NxTitle>
|
||||||
|
|
||||||
<Forms.FormText variant="text-md/normal" className={Margins.bottom20}>
|
<Forms.FormText variant="text-md/normal" className={Margins.bottom20}>
|
||||||
Synchronize your settings to the cloud. This allows easy synchronization across multiple devices with
|
Synchronize your settings to the cloud. This allows easy synchronization across multiple devices with
|
||||||
minimal effort.
|
minimal effort.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
<Switch
|
<FormSwitch
|
||||||
key="cloud-sync"
|
key="cloud-sync"
|
||||||
disabled={!cloud.authenticated}
|
title="Settings Sync"
|
||||||
value={cloud.settingsSync}
|
value={cloud.settingsSync}
|
||||||
onChange={v => { cloud.settingsSync = v; }}
|
onChange={v => { cloud.settingsSync = v; }}
|
||||||
className="nx-removeSwitchDivider"
|
className="nx-removeSwitchDivider"
|
||||||
>
|
disabled={!cloud.authenticated}
|
||||||
Settings Sync
|
/>
|
||||||
</Switch>
|
<div className="vc-cloud-settings-sync-grid">
|
||||||
<div className="nx-cloud-settings-sync-grid">
|
|
||||||
<Button
|
<Button
|
||||||
size={Button.Sizes.SMALL}
|
size={Button.Sizes.SMALL}
|
||||||
disabled={!sectionEnabled}
|
disabled={!sectionEnabled}
|
||||||
@@ -117,6 +119,7 @@ function SettingsSyncSection() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</NxCard>
|
</NxCard>
|
||||||
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,15 +128,20 @@ function CloudTab() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsTab title="Vencord Cloud">
|
<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.
|
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
|
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
|
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.
|
can host it yourself. It may or may not work with Nexulien; use with caution.
|
||||||
|
</NxText>
|
||||||
</NxCard>
|
</NxCard>
|
||||||
|
|
||||||
<Switch
|
<FormSwitch
|
||||||
key="backend"
|
key="backend"
|
||||||
|
title="Enable Cloud Integrations"
|
||||||
|
description="This will request authorization if you have not yet set up cloud integrations."
|
||||||
value={settings.cloud.authenticated}
|
value={settings.cloud.authenticated}
|
||||||
onChange={v => {
|
onChange={v => {
|
||||||
if (v)
|
if (v)
|
||||||
@@ -141,15 +149,12 @@ function CloudTab() {
|
|||||||
else
|
else
|
||||||
settings.cloud.authenticated = v;
|
settings.cloud.authenticated = v;
|
||||||
}}
|
}}
|
||||||
note="This will request authorization if you have not yet set up cloud integrations."
|
hideBorder={!settings.cloud.authenticated}
|
||||||
className={!settings.cloud.authenticated ? "nx-removeSwitchDivider" : ""}
|
/>
|
||||||
>
|
|
||||||
Enable Cloud Integrations
|
|
||||||
</Switch>
|
|
||||||
|
|
||||||
{settings.cloud.authenticated ? <>
|
{settings.cloud.authenticated ? <>
|
||||||
<NxCard>
|
<NxCard>
|
||||||
<NxCardTitle>Backend URL</NxCardTitle>
|
<NxTitle>Backend URL</NxTitle>
|
||||||
<Forms.FormText className={Margins.bottom8}>
|
<Forms.FormText className={Margins.bottom8}>
|
||||||
Which backend to use when using cloud integrations.
|
Which backend to use when using cloud integrations.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
@@ -185,7 +190,7 @@ function CloudTab() {
|
|||||||
body: "Once your data is erased, we cannot recover it. There's no going back!",
|
body: "Once your data is erased, we cannot recover it. There's no going back!",
|
||||||
onConfirm: eraseAllData,
|
onConfirm: eraseAllData,
|
||||||
confirmText: "Erase it!",
|
confirmText: "Erase it!",
|
||||||
confirmColor: "nx-cloud-erase-data-danger-btn",
|
confirmColor: "vc-cloud-erase-data-danger-btn",
|
||||||
cancelText: "Nevermind"
|
cancelText: "Nevermind"
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
@@ -195,6 +200,7 @@ function CloudTab() {
|
|||||||
</NxCard >
|
</NxCard >
|
||||||
<SettingsSyncSection />
|
<SettingsSyncSection />
|
||||||
</> : <></>}
|
</> : <></>}
|
||||||
|
</section>
|
||||||
</SettingsTab>
|
</SettingsTab>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,11 @@ import { Settings, useSettings } from "@api/Settings";
|
|||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons";
|
import { FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons";
|
||||||
import { Link } from "@components/Link";
|
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 { QuickAction, QuickActionContainer } from "@components/settings/QuickAction";
|
||||||
import { openPluginModal } from "@components/settings/tabs/plugins/PluginModal";
|
import { openPluginModal } from "@components/settings/tabs/plugins/PluginModal";
|
||||||
import { UserThemeHeader } from "@main/themes";
|
import { UserThemeHeader } from "@main/themes";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
|
||||||
import { findLazy } from "@webpack";
|
import { findLazy } from "@webpack";
|
||||||
import { Forms, useEffect, useRef, useState } from "@webpack/common";
|
import { Forms, useEffect, useRef, useState } from "@webpack/common";
|
||||||
import ClientThemePlugin from "plugins/clientTheme";
|
import ClientThemePlugin from "plugins/clientTheme";
|
||||||
@@ -21,7 +20,7 @@ import type { ComponentType, Ref, SyntheticEvent } from "react";
|
|||||||
|
|
||||||
import { ThemeCard } from "./ThemeCard";
|
import { ThemeCard } from "./ThemeCard";
|
||||||
|
|
||||||
const cl = classNameFactory("nx-settings-theme-");
|
const cl = classNameFactory("vc-settings-theme-");
|
||||||
|
|
||||||
type FileInput = ComponentType<{
|
type FileInput = ComponentType<{
|
||||||
ref: Ref<HTMLInputElement>;
|
ref: Ref<HTMLInputElement>;
|
||||||
@@ -86,7 +85,7 @@ export function LocalThemesTab() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NxCard className={cl("info-card")}>
|
<NxCard className={cl("info-card")}>
|
||||||
<NxCardTitle>Find Themes:</NxCardTitle>
|
<NxTitle>Find Themes:</NxTitle>
|
||||||
<div style={{ marginBottom: ".5em", display: "flex", flexDirection: "column" }}>
|
<div style={{ marginBottom: ".5em", display: "flex", flexDirection: "column" }}>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
@@ -101,16 +100,20 @@ export function LocalThemesTab() {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<NxCard className={classes("nx-card-help", Margins.bottom16)}>
|
<NxCard variant="help" className={Margins.bottom16}>
|
||||||
<NxCardTitle>External Resources</NxCardTitle>
|
<NxTitle>External Resources</NxTitle>
|
||||||
<span>For security reasons, loading resources (styles, fonts, images, ...) from most sites is blocked.</span>
|
<NxText>
|
||||||
<span> Make sure all your assets are hosted on GitHub, GitLab, Codeberg, Imgur, Discord or Google Fonts.</span>
|
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>
|
</NxCard>
|
||||||
|
|
||||||
<Forms.FormSection title="Local Themes">
|
|
||||||
|
<section>
|
||||||
|
<Forms.FormTitle tag="h5">Local Themes</Forms.FormTitle>
|
||||||
<QuickActionContainer title="Manage Local Themes">
|
<QuickActionContainer title="Manage Local Themes">
|
||||||
<>
|
<>
|
||||||
{IS_WEB ?
|
{IS_WEB ?
|
||||||
@@ -175,7 +178,7 @@ export function LocalThemesTab() {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Forms.FormSection>
|
</section>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useSettings } from "@api/Settings";
|
import { useSettings } from "@api/Settings";
|
||||||
import { NxCard, NxCardTitle } from "@components/NxCard";
|
import { NxCard, NxText, NxTitle } from "@components/NxComponents";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
|
||||||
import { Forms, TextArea, useState } from "@webpack/common";
|
import { Forms, TextArea, useState } from "@webpack/common";
|
||||||
|
|
||||||
export function OnlineThemesTab() {
|
export function OnlineThemesTab() {
|
||||||
@@ -28,30 +27,33 @@ export function OnlineThemesTab() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NxCard className={`${classes("nx-card-warning", Margins.bottom16)}`}>
|
<NxCard variant="warning" className={Margins.bottom16}>
|
||||||
<span>
|
<NxText>
|
||||||
This section is for advanced users. If you are having difficulties using it, use the
|
This section is for advanced users. If you are having difficulties using it, use the
|
||||||
Local Themes tab instead.
|
Local Themes tab instead.
|
||||||
</span>
|
</NxText>
|
||||||
</NxCard>
|
</NxCard>
|
||||||
<NxCard className="nx-settings-card">
|
<NxCard className="vc-settings-card">
|
||||||
<NxCardTitle tag="h5">Paste links to css files here</NxCardTitle>
|
<NxTitle>Paste links to css files here</NxTitle>
|
||||||
<span>One link per line</span><br></br>
|
<NxText>
|
||||||
<span>You can prefix lines with @light or @dark to toggle them based on your Discord theme</span><br></br>
|
<span>— One link per line</span><br></br>
|
||||||
<span>Make sure to use direct links to files (raw or github.io)!</span>
|
<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>
|
</NxCard>
|
||||||
|
|
||||||
<Forms.FormSection title="Online Themes" tag="h5">
|
<section>
|
||||||
|
<Forms.FormTitle tag="h5">Online Themes</Forms.FormTitle>
|
||||||
<TextArea
|
<TextArea
|
||||||
value={themeText}
|
value={themeText}
|
||||||
onChange={setThemeText}
|
onChange={setThemeText}
|
||||||
className={"nx-settings-theme-links"}
|
className={"vc-settings-theme-links"}
|
||||||
placeholder="Enter Theme Links..."
|
placeholder="Enter Theme Links..."
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
rows={10}
|
rows={10}
|
||||||
/>
|
/>
|
||||||
</Forms.FormSection>
|
</section>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
|
||||||
import { Link } from "@components/Link";
|
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 { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||||
import { getStylusWebStoreUrl } from "@utils/web";
|
import { getStylusWebStoreUrl } from "@utils/web";
|
||||||
import { React, TabBar, useState } from "@webpack/common";
|
import { React, TabBar, useState } from "@webpack/common";
|
||||||
@@ -41,18 +41,18 @@ function ThemesTab() {
|
|||||||
<TabBar
|
<TabBar
|
||||||
type="top"
|
type="top"
|
||||||
look="brand"
|
look="brand"
|
||||||
className="nx-settings-tab-bar"
|
className="vc-settings-tab-bar"
|
||||||
selectedItem={currentTab}
|
selectedItem={currentTab}
|
||||||
onItemSelect={setCurrentTab}
|
onItemSelect={setCurrentTab}
|
||||||
>
|
>
|
||||||
<TabBar.Item
|
<TabBar.Item
|
||||||
className="nx-settings-tab-bar-item"
|
className="vc-settings-tab-bar-item"
|
||||||
id={ThemeTab.LOCAL}
|
id={ThemeTab.LOCAL}
|
||||||
>
|
>
|
||||||
Local Themes
|
Local Themes
|
||||||
</TabBar.Item>
|
</TabBar.Item>
|
||||||
<TabBar.Item
|
<TabBar.Item
|
||||||
className="nx-settings-tab-bar-item"
|
className="vc-settings-tab-bar-item"
|
||||||
id={ThemeTab.ONLINE}
|
id={ThemeTab.ONLINE}
|
||||||
>
|
>
|
||||||
Online Themes
|
Online Themes
|
||||||
@@ -71,7 +71,7 @@ function UserscriptThemesTab() {
|
|||||||
return (
|
return (
|
||||||
<SettingsTab title="Themes">
|
<SettingsTab title="Themes">
|
||||||
<NxCard>
|
<NxCard>
|
||||||
<NxCardTitle>Themes are not supported on the Userscript!</NxCardTitle>
|
<NxTitle>Themes are not supported on the Userscript!</NxTitle>
|
||||||
<span>
|
<span>
|
||||||
You can instead install themes with the <Link href={getStylusWebStoreUrl()}>Stylus extension</Link>!
|
You can instead install themes with the <Link href={getStylusWebStoreUrl()}>Stylus extension</Link>!
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
.nx-settings-theme-grid {
|
.vc-settings-theme-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: 16px;
|
grid-gap: 16px;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-theme-card {
|
.vc-settings-theme-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: var(--background-base-lower-alt);
|
background-color: var(--background-base-lower-alt);
|
||||||
@@ -16,12 +16,12 @@
|
|||||||
transition-property: box-shadow, transform, background, opacity;
|
transition-property: box-shadow, transform, background, opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-theme-info-card {
|
.vc-settings-theme-info-card {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-theme-card-text {
|
.vc-settings-theme-card-text {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
height: 1.2em;
|
height: 1.2em;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
@@ -29,21 +29,21 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-theme-author::before {
|
.vc-settings-theme-author::before {
|
||||||
content: "by ";
|
content: "by ";
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-theme-validator-card {
|
.vc-settings-theme-validator-card {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-csp-list {
|
.vc-settings-csp-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-settings-csp-row {
|
.vc-settings-csp-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import { ErrorCard } from "@components/ErrorCard";
|
import { ErrorCard } from "@components/ErrorCard";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
import { NxCard, NxCardTitle } from "@components/NxCard";
|
import { NxCard, NxTitle } from "@components/NxComponents";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { relaunch } from "@utils/native";
|
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; }) {
|
export function Changes({ updates, repo, repoPending }: CommonProps & { updates: typeof changes; }) {
|
||||||
return (
|
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 }) => (
|
{updates.map(({ hash, author, message }) => (
|
||||||
<div
|
<div
|
||||||
key={hash}
|
key={hash}
|
||||||
@@ -81,7 +81,7 @@ export function Updatable(props: CommonProps) {
|
|||||||
|
|
||||||
return (
|
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 ? (
|
{!updates && updateError ? (
|
||||||
<>
|
<>
|
||||||
<Forms.FormText>Failed to check for updates. Check the console for more info</Forms.FormText>
|
<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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<NxCard>
|
<NxCard>
|
||||||
<NxCardTitle>Repository</NxCardTitle>
|
<NxTitle>Repository</NxTitle>
|
||||||
|
|
||||||
<Forms.FormText className="nx-text-selectable">
|
<Forms.FormText className="vc-text-selectable">
|
||||||
{repoPending
|
{repoPending
|
||||||
? repo
|
? repo
|
||||||
: err
|
: err
|
||||||
@@ -167,7 +167,7 @@ export function Repository({ repo, repoPending, err }: CommonProps) {
|
|||||||
</Link>
|
</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>
|
</Forms.FormText>
|
||||||
</NxCard>
|
</NxCard>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -19,11 +19,12 @@
|
|||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
|
||||||
import { useSettings } from "@api/Settings";
|
import { useSettings } from "@api/Settings";
|
||||||
|
import { FormSwitch } from "@components/FormSwitch";
|
||||||
import { handleSettingsTabError, SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
import { handleSettingsTabError, SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||||
import { ModalCloseButton, ModalContent, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
import { ModalCloseButton, ModalContent, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { getRepo, isNewer, UpdateLogger } from "@utils/updater";
|
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";
|
import { CommonProps, Newer, Repository, Updatable } from "./Components";
|
||||||
|
|
||||||
@@ -45,21 +46,19 @@ function Updater() {
|
|||||||
<SettingsTab title="Nexulien Updater">
|
<SettingsTab title="Nexulien Updater">
|
||||||
<Forms.FormTitle tag="h5">Updater Settings</Forms.FormTitle>
|
<Forms.FormTitle tag="h5">Updater Settings</Forms.FormTitle>
|
||||||
|
|
||||||
<Switch
|
<FormSwitch
|
||||||
|
title="Automatically update"
|
||||||
|
description="Automatically update Vencord without confirmation prompt"
|
||||||
value={settings.autoUpdate}
|
value={settings.autoUpdate}
|
||||||
onChange={(v: boolean) => settings.autoUpdate = v}
|
onChange={(v: boolean) => settings.autoUpdate = v}
|
||||||
note="Automatically update Vencord without confirmation prompt"
|
/>
|
||||||
>
|
<FormSwitch
|
||||||
Automatically update
|
title="Get notified when an automatic update completes"
|
||||||
</Switch>
|
description="Show a notification when Vencord automatically updates"
|
||||||
<Switch
|
|
||||||
value={settings.autoUpdateNotification}
|
value={settings.autoUpdateNotification}
|
||||||
onChange={(v: boolean) => settings.autoUpdateNotification = v}
|
onChange={(v: boolean) => settings.autoUpdateNotification = v}
|
||||||
note="Show a notification when Vencord automatically updates"
|
|
||||||
disabled={!settings.autoUpdate}
|
disabled={!settings.autoUpdate}
|
||||||
>
|
/>
|
||||||
Get notified when an automatic update completes
|
|
||||||
</Switch>
|
|
||||||
|
|
||||||
<Forms.FormTitle tag="h5">Updates</Forms.FormTitle>
|
<Forms.FormTitle tag="h5">Updates</Forms.FormTitle>
|
||||||
|
|
||||||
@@ -85,8 +84,8 @@ export const openUpdaterModal = IS_UPDATER_DISABLED
|
|||||||
try {
|
try {
|
||||||
openModal(wrapTab((modalProps: ModalProps) => (
|
openModal(wrapTab((modalProps: ModalProps) => (
|
||||||
<ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
<ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||||
<ModalContent className="nx-updater-modal">
|
<ModalContent className="vc-updater-modal">
|
||||||
<ModalCloseButton onClick={modalProps.onClose} className="nx-updater-modal-close-button" />
|
<ModalCloseButton onClick={modalProps.onClose} className="vc-updater-modal-close-button" />
|
||||||
<UpdaterTab />
|
<UpdaterTab />
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</ModalRoot>
|
</ModalRoot>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.nx-updater-changes {
|
.vc-updater-changes {
|
||||||
background: var(--background-code);
|
background: var(--background-code);
|
||||||
border: 1px solid var(--border-normal);
|
border: 1px solid var(--border-normal);
|
||||||
margin-top: 16px !important;
|
margin-top: 16px !important;
|
||||||
@@ -6,21 +6,10 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-updater-modal {
|
.vc-updater-modal {
|
||||||
padding: 1.5em !important;
|
padding: 1.5em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-updater-modal-close-button {
|
.vc-updater-modal-close-button {
|
||||||
float: right;
|
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 { useSettings } from "@api/Settings";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { identity } from "@utils/misc";
|
import { identity } from "@utils/misc";
|
||||||
import { Forms, Select } from "@webpack/common";
|
import { Forms, Select } from "@webpack/common";
|
||||||
@@ -15,6 +16,7 @@ export function VibrancySettings() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Forms.FormTitle tag="h5">Window vibrancy style (requires restart)</Forms.FormTitle>
|
<Forms.FormTitle tag="h5">Window vibrancy style (requires restart)</Forms.FormTitle>
|
||||||
|
<ErrorBoundary noop>
|
||||||
<Select
|
<Select
|
||||||
className={Margins.bottom20}
|
className={Margins.bottom20}
|
||||||
placeholder="Window vibrancy style"
|
placeholder="Window vibrancy style"
|
||||||
@@ -74,7 +76,9 @@ export function VibrancySettings() {
|
|||||||
]}
|
]}
|
||||||
select={v => settings.macosVibrancyStyle = v}
|
select={v => settings.macosVibrancyStyle = v}
|
||||||
isSelected={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() {
|
export function NotificationSection() {
|
||||||
return (
|
return (
|
||||||
<Forms.FormSection className={Margins.top16} title="Vencord Notifications" tag="h5">
|
<section className={Margins.top16}>
|
||||||
|
<Forms.FormTitle tag="h5">Vencord Notifications</Forms.FormTitle>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Button onClick={openNotificationSettingsModal}>
|
<Button onClick={openNotificationSettingsModal}>
|
||||||
Notification Settings
|
Notification Settings
|
||||||
@@ -24,7 +25,7 @@ export function NotificationSection() {
|
|||||||
View Notification Log
|
View Notification Log
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Forms.FormSection>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ import "@components/settings/styles.css";
|
|||||||
|
|
||||||
import { Settings, useSettings } from "@api/Settings";
|
import { Settings, useSettings } from "@api/Settings";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import { FormSwitch } from "@components/FormSwitch";
|
||||||
import { FolderIcon, GithubIcon, PaintbrushIcon, RestartIcon } from "@components/index";
|
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 { NxMascot } from "@components/settings/Mascot";
|
||||||
import { QuickAction, QuickActionContainer } from "@components/settings/QuickAction";
|
import { QuickAction, QuickActionContainer } from "@components/settings/QuickAction";
|
||||||
import { SpecialCard } from "@components/settings/SpecialCard";
|
import { SpecialCard } from "@components/settings/SpecialCard";
|
||||||
@@ -33,14 +34,14 @@ import { gitRemote } from "@shared/vencordUserAgent";
|
|||||||
import { IS_MAC, IS_WINDOWS } from "@utils/constants";
|
import { IS_MAC, IS_WINDOWS } from "@utils/constants";
|
||||||
import { openInviteModal } from "@utils/discord";
|
import { openInviteModal } from "@utils/discord";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { isPluginDev } from "@utils/misc";
|
import { classes, isPluginDev } from "@utils/misc";
|
||||||
import { closeAllModals } from "@utils/modal";
|
import { closeAllModals } from "@utils/modal";
|
||||||
import { relaunch } from "@utils/native";
|
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";
|
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_IMAGE = "https://cdn.discordapp.com/emojis/1337858798664024156.png";
|
||||||
const CONTRIB_BACKGROUND_IMAGE = "https://media.discordapp.net/stickers/1337878381517078649.png?size=2048";
|
const CONTRIB_BACKGROUND_IMAGE = "https://media.discordapp.net/stickers/1337878381517078649.png?size=2048";
|
||||||
@@ -94,14 +95,13 @@ function Switches() {
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
return Switches.map(s => s && (
|
return Switches.map(s => s && (
|
||||||
<Switch
|
<FormSwitch
|
||||||
key={s.key}
|
key={s.key}
|
||||||
|
title={s.title}
|
||||||
|
description={s.note}
|
||||||
value={settings[s.key]}
|
value={settings[s.key]}
|
||||||
onChange={v => settings[s.key] = v}
|
onChange={v => settings[s.key] = v}
|
||||||
note={s.note}
|
/>
|
||||||
>
|
|
||||||
{s.title}
|
|
||||||
</Switch>
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,15 +116,19 @@ function VencordSettings() {
|
|||||||
<>
|
<>
|
||||||
<SettingsTab title="Nexulien Settings">
|
<SettingsTab title="Nexulien Settings">
|
||||||
<HeaderCard />
|
<HeaderCard />
|
||||||
|
|
||||||
{isPluginDev(user?.id) && !hideContributorCard && (
|
{isPluginDev(user?.id) && !hideContributorCard && (
|
||||||
<SpecialCard
|
<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!"
|
description="Since you've contributed to Nexulien, you now have a cool new badge!"
|
||||||
cardImage={CONTRIB_IMAGE}
|
cardImage={CONTRIB_IMAGE}
|
||||||
backgroundImage={CONTRIB_BACKGROUND_IMAGE}
|
backgroundImage={CONTRIB_BACKGROUND_IMAGE}
|
||||||
backgroundGradient="linear-gradient(to left, var(--nx-green), var(--nx-purple))"
|
backgroundGradient="linear-gradient(to left, var(--nx-green), var(--nx-purple))"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<section>
|
||||||
<QuickActionContainer title="Quick Actions" columns="2">
|
<QuickActionContainer title="Quick Actions" columns="2">
|
||||||
<QuickAction
|
<QuickAction
|
||||||
Icon={PaintbrushIcon}
|
Icon={PaintbrushIcon}
|
||||||
@@ -150,33 +154,31 @@ function VencordSettings() {
|
|||||||
action={() => VencordNative.native.openExternal("https://github.com/" + gitRemote)}
|
action={() => VencordNative.native.openExternal("https://github.com/" + gitRemote)}
|
||||||
/>
|
/>
|
||||||
</QuickActionContainer>
|
</QuickActionContainer>
|
||||||
|
</section>
|
||||||
|
|
||||||
<Forms.FormSection className={Margins.top16} title="Settings" tag="h5">
|
<section className={Margins.top16}>
|
||||||
|
<Forms.FormTitle tag="h5">Settings</Forms.FormTitle>
|
||||||
{showHint ?
|
{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
|
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
|
{" "}<a onClick={() => openPluginModal(Vencord.Plugins.plugins.Settings)}>
|
||||||
style={{ all: undefined, color: "var(--text-link)", display: "inline-block", backgroundColor: "transparent", padding: 0, fontSize: 16 }}
|
|
||||||
onClick={() => openPluginModal(Vencord.Plugins.plugins.Settings)}
|
|
||||||
>
|
|
||||||
settings of the Settings plugin
|
settings of the Settings plugin
|
||||||
</button>!
|
</a>!
|
||||||
|
</NxText>
|
||||||
</NxCard> : <></>}
|
</NxCard> : <></>}
|
||||||
|
|
||||||
<Switches />
|
<Switches />
|
||||||
</Forms.FormSection>
|
|
||||||
|
|
||||||
|
|
||||||
{needsVibrancySettings && <VibrancySettings />}
|
{needsVibrancySettings && <VibrancySettings />}
|
||||||
</SettingsTab>
|
</section>
|
||||||
{BackupAndRestoreTab()}
|
{BackupAndRestoreTab()}
|
||||||
|
</SettingsTab>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function nexulien() {
|
function nexulien() {
|
||||||
const audioElement = document.createElement("audio");
|
const audioElement = document.createElement("audio");
|
||||||
const logo = document.getElementById("nx-settings-logo");
|
const logo = document.getElementById("vc-settings-logo");
|
||||||
|
|
||||||
const audioArray = [
|
const audioArray = [
|
||||||
"https://raw.githubusercontent.com/Nexulien/Assets/main/tts/bonzi.wav", // 🟣🐒
|
"https://raw.githubusercontent.com/Nexulien/Assets/main/tts/bonzi.wav", // 🟣🐒
|
||||||
@@ -190,7 +192,7 @@ function nexulien() {
|
|||||||
audioElement.volume = 0.5;
|
audioElement.volume = 0.5;
|
||||||
audioElement.play();
|
audioElement.play();
|
||||||
window.setTimeout(function () {
|
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);
|
}, 200);
|
||||||
window.setTimeout(function () {
|
window.setTimeout(function () {
|
||||||
logo!.removeAttribute("style");
|
logo!.removeAttribute("style");
|
||||||
@@ -213,8 +215,8 @@ function HeaderCard() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{headerCardSize !== "none" ?
|
{headerCardSize !== "none" &&
|
||||||
<NxCard className={cl("card", "header", headerCardSize === "minimal" ? "header-minimal" : "")}>
|
<NxCard className={classes(cl("card", "header", headerCardSize === "minimal" && "header-minimal"), "nx-card-grand")}>
|
||||||
<div>
|
<div>
|
||||||
<span className={cl("logo-container")} onClick={() => nexulien()}>
|
<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")}>
|
<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>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{headerCardSize === "default" ? <>
|
{headerCardSize === "default" ? <NxText>
|
||||||
{/* ↓ Factual Information */}
|
{/* ↓ 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>
|
<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
|
<Button
|
||||||
size={headerCardSize === "minimal" ? Button.Sizes.SMALL : Button.Sizes.MEDIUM}
|
size={headerCardSize === "minimal" ? Button.Sizes.SMALL : Button.Sizes.MEDIUM}
|
||||||
onClick={() => window.open("https://github.com/Nexulien")}
|
onClick={() => window.open("https://github.com/Nexulien")}
|
||||||
@@ -258,9 +260,9 @@ function HeaderCard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{headerCardSize === "default" ? <NxMascot /> : <></>}
|
{headerCardSize === "default" && <NxMascot />}
|
||||||
</NxCard>
|
</NxCard>
|
||||||
: <></>}
|
}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,24 @@ import * as Webpack from "@webpack";
|
|||||||
import { wreq } from "@webpack";
|
import { wreq } from "@webpack";
|
||||||
import { AnyModuleFactory } 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() {
|
export async function loadLazyChunks() {
|
||||||
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
|
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
|
// All chunks Discord has mapped to asset files, even if they are not used anymore
|
||||||
const allChunks = [] as PropertyKey[];
|
const chunkMap = getWebpackChunkMap();
|
||||||
|
if (!chunkMap) throw new Error("Failed to get chunk map");
|
||||||
// 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 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");
|
if (allChunks.length === 0) throw new Error("Failed to get all chunks");
|
||||||
|
|
||||||
// Chunks which our regex could not catch to load
|
// 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/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { LoDashStatic } from "lodash";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
/**
|
/**
|
||||||
* This exists only at build time, so references to it in patches should insert it
|
* 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 Vesktop: any;
|
||||||
export var VesktopNative: any;
|
export var VesktopNative: any;
|
||||||
|
|
||||||
interface Window extends Record<PropertyKey, any> {
|
interface Window extends Record<PropertyKey, any> { }
|
||||||
_: LoDashStatic;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { };
|
export { };
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ const patchCsp = (headers: PolicyMap) => {
|
|||||||
pushDirective("script-src", "'unsafe-inline'", "'unsafe-eval'");
|
pushDirective("script-src", "'unsafe-inline'", "'unsafe-eval'");
|
||||||
|
|
||||||
for (const directive of ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"]) {
|
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)) {
|
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/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// <reference types="standalone-electron-types"/>
|
|
||||||
|
|
||||||
declare module "~plugins" {
|
declare module "~plugins" {
|
||||||
const plugins: Record<string, import("./utils/types").Plugin>;
|
const plugins: Record<string, import("./utils/types").Plugin>;
|
||||||
export default plugins;
|
export default plugins;
|
||||||
@@ -28,11 +26,6 @@ declare module "~plugins" {
|
|||||||
export const ExcludedPlugins: Record<string, "web" | "discordDesktop" | "vesktop" | "desktop" | "dev">;
|
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" {
|
declare module "~git-hash" {
|
||||||
const hash: string;
|
const hash: string;
|
||||||
export default hash;
|
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 {
|
.nx-badge-modal-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
margin: 16px;
|
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
padding: 8px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-badge-modal-badge {
|
.nx-badge-modal-badge {
|
||||||
display: flex;
|
display: flex;
|
||||||
background: linear-gradient(135deg, #70e, #0f9);
|
|
||||||
|
/* background: linear-gradient(135deg, #70e, #0f9); */
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
height: 96px;
|
height: 96px;
|
||||||
width: 96px;
|
|
||||||
min-width: 96px;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
margin-left: 8px;
|
||||||
|
align-self: center;
|
||||||
.nx-badge-modal-badge::after {
|
|
||||||
position: absolute;
|
|
||||||
content: "";
|
|
||||||
width: 88px;
|
|
||||||
height: 88px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: var(--modal-background);;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-badge-modal-badge > img {
|
.nx-badge-modal-badge > img {
|
||||||
display: block;
|
display: block;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
|
width: 64px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-badge-modal-header > div {
|
.nx-badge-modal-header > div {
|
||||||
@@ -40,14 +38,21 @@
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-badge-modal-description {
|
.nx-badge-modal-header > div > div[data-text-variant="text-sm/normal"] {
|
||||||
padding: 16px;
|
color: var(--text-secondary) !important;
|
||||||
background-color: var(--input-background);
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid var(--input-border);
|
|
||||||
color: var(--text-normal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nx-badge-modal-badge.yucky-vencord {
|
.nx-badge-modal-badge-divider {
|
||||||
background: none;
|
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 "./fixDiscordBadgePadding.css";
|
||||||
import "./badgeModal.css";
|
|
||||||
|
|
||||||
import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
|
import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
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 { openContributorModal } from "@components/settings/tabs";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { Margins } from "@utils/margins";
|
|
||||||
import { copyWithToast, shouldShowContributorBadge } from "@utils/misc";
|
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 definePlugin from "@utils/types";
|
||||||
import { User } from "@vencord/discord-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";
|
const CONTRIBUTOR_BADGE = "https://raw.githubusercontent.com/Nexulien/assets/main/badges/contributor.png";
|
||||||
@@ -124,6 +118,13 @@ export default definePlugin({
|
|||||||
replace: "...$self.getBadgeMouseEventHandlers($1),$&"
|
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) {
|
getDonorBadges(userId: string) {
|
||||||
if (userId !== "343383572805058560") {
|
|
||||||
return DonorBadges[userId]?.map(badge => ({
|
return DonorBadges[userId]?.map(badge => ({
|
||||||
image: badge.badge,
|
image: badge.badge,
|
||||||
description: badge.tooltip,
|
description: badge.tooltip,
|
||||||
@@ -206,58 +206,11 @@ export default definePlugin({
|
|||||||
closeModal(modalKey);
|
closeModal(modalKey);
|
||||||
VencordNative.native.openExternal("https://github.com/sponsors/Vendicated");
|
VencordNative.native.openExternal("https://github.com/sponsors/Vendicated");
|
||||||
}}>
|
}}>
|
||||||
<ModalRoot {...props}>
|
<BadgeModal badge={badge} props={props} nxBadge={false}></BadgeModal>
|
||||||
<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>
|
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getNexulienBadges(userId: string) {
|
getNexulienBadges(userId: string) {
|
||||||
@@ -280,54 +233,7 @@ export default definePlugin({
|
|||||||
closeModal(modalKey);
|
closeModal(modalKey);
|
||||||
VencordNative.native.openExternal("https://github.com/Nexulien/assets/blob/main/badges.json");
|
VencordNative.native.openExternal("https://github.com/Nexulien/assets/blob/main/badges.json");
|
||||||
}}>
|
}}>
|
||||||
<ModalRoot {...props}>
|
<BadgeModal badge={badge} props={props} nxBadge={true}></BadgeModal>
|
||||||
<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>
|
|
||||||
</ErrorBoundary>
|
</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,
|
startAt: StartAt.Init,
|
||||||
start() {
|
start() {
|
||||||
// Sentry is initialized in its own WebpackInstance.
|
// Sentry is initialized in its own WebpackInstance.
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default definePlugin({
|
|||||||
find: ".versionHash",
|
find: ".versionHash",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /\[\(0,\i\.jsxs?\)\((.{1,10}),(\{[^{}}]+\{.{0,20}.versionHash,.+?\})\)," "/,
|
match: /\.info.+?\[\(0,\i\.jsxs?\)\((.{1,10}),(\{[^{}}]+\{.{0,20}.versionHash,.+?\})\)," "/,
|
||||||
replace: (m, component, props) => {
|
replace: (m, component, props) => {
|
||||||
props = props.replace(/children:\[.+\]/, "");
|
props = props.replace(/children:\[.+\]/, "");
|
||||||
return `${m},$self.makeInfoElements(${component}, ${props})`;
|
return `${m},$self.makeInfoElements(${component}, ${props})`;
|
||||||
@@ -67,8 +67,9 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
|
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.\i\).*?(\i\.\i)\.open\()/,
|
// Skip the check Discord performs to make sure the section being selected in the user settings context menu is valid
|
||||||
replace: "$2.open($1);return;"
|
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,
|
section: SectionTypes.HEADER,
|
||||||
label: "Nexulien",
|
label: "Nexulien",
|
||||||
className: "nx-settings-header"
|
className: "vc-settings-header"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
section: "settings/tabs",
|
section: "settings/tabs",
|
||||||
label: "Settings",
|
label: "Settings",
|
||||||
element: VencordTab,
|
element: VencordTab,
|
||||||
className: "nx-settings"
|
className: "vc-settings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
section: "NexulienPlugins",
|
section: "NexulienPlugins",
|
||||||
label: "Plugins",
|
label: "Plugins",
|
||||||
element: PluginsTab,
|
element: PluginsTab,
|
||||||
className: "nx-plugins"
|
className: "vc-plugins"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
section: "NexulienThemes",
|
section: "NexulienThemes",
|
||||||
label: "Themes",
|
label: "Themes",
|
||||||
element: ThemesTab,
|
element: ThemesTab,
|
||||||
className: "nx-themes"
|
className: "vc-themes"
|
||||||
},
|
},
|
||||||
!IS_UPDATER_DISABLED && {
|
!IS_UPDATER_DISABLED && {
|
||||||
section: "NexulienUpdater",
|
section: "NexulienUpdater",
|
||||||
label: "Updater",
|
label: "Updater",
|
||||||
element: UpdaterTab,
|
element: UpdaterTab,
|
||||||
className: "nx-updater"
|
className: "vc-updater"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
section: "NexulienNotifications",
|
section: "NexulienNotifications",
|
||||||
label: "Notifications",
|
label: "Notifications",
|
||||||
element: NotificationsTab,
|
element: NotificationsTab,
|
||||||
className: "nx-settings"
|
className: "nx-notifications"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
section: "VencordCloud",
|
section: "VencordCloud",
|
||||||
|
|||||||
@@ -7,49 +7,12 @@
|
|||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs, IS_MAC } from "@utils/constants";
|
import { Devs, IS_MAC } from "@utils/constants";
|
||||||
import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types";
|
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";
|
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
|
||||||
|
|
||||||
const Native = VencordNative.pluginHelpers.AppleMusicRichPresence as PluginNative<typeof import("./native")>;
|
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 {
|
export interface TrackData {
|
||||||
name: string;
|
name: string;
|
||||||
album?: string;
|
album?: string;
|
||||||
@@ -90,6 +53,25 @@ const settings = definePluginSettings({
|
|||||||
{ label: "Listening", value: ActivityType.LISTENING }
|
{ 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: {
|
refreshInterval: {
|
||||||
type: OptionType.SLIDER,
|
type: OptionType.SLIDER,
|
||||||
description: "The interval between activity refreshes (seconds)",
|
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,
|
metadata: !isRadio && buttons.length ? { button_urls: buttons.map(v => v.url) } : undefined,
|
||||||
|
|
||||||
type: settings.store.activityType,
|
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
|
* 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 { execFile } from "child_process";
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
|
|
||||||
@@ -26,24 +26,6 @@ interface RemoteData {
|
|||||||
|
|
||||||
let cachedRemoteData: { id: string, data: RemoteData; } | { id: string, failures: number; } | null = null;
|
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; }) {
|
async function fetchRemoteData({ id, name, artist, album }: { id: string, name: string, artist: string, album: string; }) {
|
||||||
if (id === cachedRemoteData?.id) {
|
if (id === cachedRemoteData?.id) {
|
||||||
if ("data" in cachedRemoteData) return cachedRemoteData.data;
|
if ("data" in cachedRemoteData) return cachedRemoteData.data;
|
||||||
@@ -51,36 +33,34 @@ async function fetchRemoteData({ id, name, artist, album }: { id: string, name:
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dataUrl = new URL("https://amp-api-edge.music.apple.com/v1/catalog/us/search");
|
const dataUrl = new URL("https://itunes.apple.com/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");
|
|
||||||
dataUrl.searchParams.set("term", `${name} ${artist} ${album}`);
|
dataUrl.searchParams.set("term", `${name} ${artist} ${album}`);
|
||||||
dataUrl.searchParams.set("include[songs]", "artists");
|
dataUrl.searchParams.set("media", "music");
|
||||||
|
dataUrl.searchParams.set("entity", "song");
|
||||||
const token = await getToken();
|
|
||||||
|
|
||||||
const songData = await fetch(dataUrl, {
|
const songData = await fetch(dataUrl, {
|
||||||
headers: {
|
headers: {
|
||||||
"accept": "*/*",
|
"user-agent": VENCORD_USER_AGENT,
|
||||||
"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",
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(r => r.json())
|
.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 = {
|
cachedRemoteData = {
|
||||||
id,
|
id,
|
||||||
data: {
|
data: {
|
||||||
appleMusicLink: songData.attributes.url,
|
appleMusicLink: songData.trackViewUrl,
|
||||||
songLink: `https://song.link/i/${songData.id}`,
|
songLink: `https://song.link/i/${new URL(songData.trackViewUrl).searchParams.get("i")}`,
|
||||||
albumArtwork: songData.attributes.artwork.url.replace("{w}x{h}", "512x512"),
|
albumArtwork: (songData.artworkUrl100).replace("100x100", "512x512"),
|
||||||
artistArtwork: songData.relationships.artists.data[0].attributes.artwork.url.replace("{w}x{h}", "512x512"),
|
artistArtwork: artistArtworkURL
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export default definePlugin({
|
|||||||
|
|
||||||
ws.onmessage = this.handleEvent;
|
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) {
|
if (!connectionSuccessful) {
|
||||||
showNotice("Failed to connect to arRPC, is it running?", "Retry", () => { // show notice about failure to connect, with retry/ignore
|
showNotice("Failed to connect to arRPC, is it running?", "Retry", () => { // show notice about failure to connect, with retry/ignore
|
||||||
popNotice();
|
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 { Animations, useStateFromStores } from "@webpack/common";
|
||||||
import type { CSSProperties } from "react";
|
import type { CSSProperties } from "react";
|
||||||
|
|
||||||
import { ExpandedGuildFolderStore, settings } from ".";
|
import { ExpandedGuildFolderStore, settings, SortedGuildStore } from ".";
|
||||||
|
|
||||||
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
||||||
const GuildsBar = findComponentByCodeLazy('("guildsnav")');
|
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 => {
|
export default ErrorBoundary.wrap(guildsBarProps => {
|
||||||
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
const expandedFolderIds = useStateFromStores([ExpandedGuildFolderStore, SortedGuildStore], () => getExpandedFolderIds());
|
||||||
const isFullscreen = useStateFromStores([ChannelRTCStore], () => ChannelRTCStore.isFullscreenInContext());
|
const isFullscreen = useStateFromStores([ChannelRTCStore], () => ChannelRTCStore.isFullscreenInContext());
|
||||||
|
|
||||||
const Sidebar = (
|
const Sidebar = (
|
||||||
<GuildsBar
|
<GuildsBar
|
||||||
{...guildsBarProps}
|
{...guildsBarProps}
|
||||||
isBetterFolders={true}
|
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(""));
|
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.
|
// 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 { Devs } from "@utils/constants";
|
||||||
import { getIntlMessage } from "@utils/discord";
|
import { getIntlMessage } from "@utils/discord";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
import { FluxDispatcher } from "@webpack/common";
|
import { FluxDispatcher } from "@webpack/common";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
@@ -35,8 +35,7 @@ enum FolderIconDisplay {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
||||||
const SortedGuildStore = findStoreLazy("SortedGuildStore");
|
export const SortedGuildStore = findStoreLazy("SortedGuildStore");
|
||||||
const GuildsTree = findLazy(m => m.prototype?.moveNextTo);
|
|
||||||
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
||||||
|
|
||||||
let lastGuildId = null as string | null;
|
let lastGuildId = null as string | null;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import { fetchNamesFromDataStore, getDefaultName, GetOsColor, GetPlatformIcon, s
|
|||||||
const AuthSessionsStore = findStoreLazy("AuthSessionsStore");
|
const AuthSessionsStore = findStoreLazy("AuthSessionsStore");
|
||||||
const UserSettingsModal = findByPropsLazy("saveAccountChanges", "open");
|
const UserSettingsModal = findByPropsLazy("saveAccountChanges", "open");
|
||||||
|
|
||||||
const TimestampClasses = findByPropsLazy("timestampTooltip", "blockquoteContainer");
|
const TimestampClasses = findByPropsLazy("timestamp", "blockquoteContainer");
|
||||||
const SessionIconClasses = findByPropsLazy("sessionIcon");
|
const SessionIconClasses = findByPropsLazy("sessionIcon");
|
||||||
|
|
||||||
const BlobMask = findComponentByCodeLazy("!1,lowerBadgeSize:");
|
const BlobMask = findComponentByCodeLazy("!1,lowerBadgeSize:");
|
||||||
@@ -92,7 +92,7 @@ export default definePlugin({
|
|||||||
<span>{title}</span>
|
<span>{title}</span>
|
||||||
{(savedSession == null || savedSession.isNew) && (
|
{(savedSession == null || savedSession.isNew) && (
|
||||||
<div
|
<div
|
||||||
className="nx-plugins-badge"
|
className="vc-plugins-badge"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: "#ED4245",
|
backgroundColor: "#ED4245",
|
||||||
marginLeft: "2px"
|
marginLeft: "2px"
|
||||||
@@ -108,7 +108,7 @@ export default definePlugin({
|
|||||||
|
|
||||||
renderTimestamp: ErrorBoundary.wrap(({ session, timeLabel }: { session: Session, timeLabel: string; }) => {
|
renderTimestamp: ErrorBoundary.wrap(({ session, timeLabel }: { session: Session, timeLabel: string; }) => {
|
||||||
return (
|
return (
|
||||||
<Tooltip text={session.approx_last_used_time.toLocaleString()} tooltipClassName={TimestampClasses.timestampTooltip}>
|
<Tooltip text={session.approx_last_used_time.toLocaleString()}>
|
||||||
{props => (
|
{props => (
|
||||||
<span {...props} className={TimestampClasses.timestamp}>
|
<span {...props} className={TimestampClasses.timestamp}>
|
||||||
{timeLabel}
|
{timeLabel}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default function PluginsSubmenu() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Menu.MenuControlItem
|
<Menu.MenuControlItem
|
||||||
id="nx-plugins-search"
|
id="vc-plugins-search"
|
||||||
control={(props, ref) => (
|
control={(props, ref) => (
|
||||||
<Menu.MenuSearchControl
|
<Menu.MenuSearchControl
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ const settings = definePluginSettings({
|
|||||||
organizeMenu: {
|
organizeMenu: {
|
||||||
description: "Organizes the settings cog context menu into categories",
|
description: "Organizes the settings cog context menu into categories",
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
default: true
|
default: true,
|
||||||
|
restartNeeded: true
|
||||||
},
|
},
|
||||||
eagerLoad: {
|
eagerLoad: {
|
||||||
description: "Removes the loading delay when opening the menu for the first time",
|
description: "Removes the loading delay when opening the menu for the first time",
|
||||||
@@ -119,12 +120,14 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
predicate: () => settings.store.eagerLoad
|
predicate: () => settings.store.eagerLoad
|
||||||
},
|
},
|
||||||
{ // Settings cog context menu
|
{
|
||||||
|
// Settings cog context menu
|
||||||
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
|
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/,
|
match: /=\[\];return (\i)(?=\.forEach)/,
|
||||||
replace: "$self.wrapMenu($1)"
|
replace: "=$self.wrapMap([]);return $self.transformSettingsEntries($1)",
|
||||||
|
predicate: () => settings.store.organizeMenu
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /case \i\.\i\.DEVELOPER_OPTIONS:return \i;/,
|
match: /case \i\.\i\.DEVELOPER_OPTIONS:return \i;/,
|
||||||
@@ -151,9 +154,7 @@ export default definePlugin({
|
|||||||
return <Layer {...props} />;
|
return <Layer {...props} />;
|
||||||
},
|
},
|
||||||
|
|
||||||
wrapMenu(list: SettingsEntry[]) {
|
transformSettingsEntries(list: SettingsEntry[]) {
|
||||||
if (!settings.store.organizeMenu) return list;
|
|
||||||
|
|
||||||
const items = [{ label: null as string | null, items: [] as SettingsEntry[] }];
|
const items = [{ label: null as string | null, items: [] as SettingsEntry[] }];
|
||||||
|
|
||||||
for (const item of list) {
|
for (const item of list) {
|
||||||
@@ -166,15 +167,13 @@ export default definePlugin({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return items;
|
||||||
filter(predicate: (item: SettingsEntry) => boolean) {
|
|
||||||
for (const category of items) {
|
|
||||||
category.items = category.items.filter(predicate);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
},
|
},
|
||||||
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)
|
.filter(a => a.items.length > 0)
|
||||||
.map(({ label, items }) => {
|
.map(({ label, items }) => {
|
||||||
const children = items.map(render);
|
const children = items.map(render);
|
||||||
@@ -184,7 +183,6 @@ export default definePlugin({
|
|||||||
key={label}
|
key={label}
|
||||||
id={label.replace(/\W/, "_")}
|
id={label.replace(/\W/, "_")}
|
||||||
label={label}
|
label={label}
|
||||||
action={children[0].props.action}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Menu.MenuItem>
|
</Menu.MenuItem>
|
||||||
@@ -193,7 +191,8 @@ export default definePlugin({
|
|||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return toWrap;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.align-chat-input [class*="panels"] [class*="inner_"],
|
[class*="panels"] [class*="inner_"],
|
||||||
.align-chat-input [class*="rtcConnectionStatus_"] {
|
[class*="rtcConnectionStatus_"] {
|
||||||
height: fit-content;
|
height: fit-content !important;
|
||||||
}
|
}
|
||||||
@@ -95,6 +95,6 @@ export default definePlugin({
|
|||||||
deps: [channelId]
|
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 { classes } from "@utils/misc";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
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 { findByCodeLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, React, UserStore } from "@webpack/common";
|
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];
|
return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ActivityAssets {
|
export const enum TimestampMode {
|
||||||
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 {
|
|
||||||
NONE,
|
NONE,
|
||||||
NOW,
|
NOW,
|
||||||
TIME,
|
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-preview-container")}>
|
||||||
<div className={cl("create-decoration-modal-form")}>
|
<div className={cl("create-decoration-modal-form")}>
|
||||||
{error !== null && <Text color="text-danger" variant="text-xs/normal">{error.message}</Text>}
|
{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
|
<FileUpload
|
||||||
filename={file?.name}
|
filename={file?.name}
|
||||||
placeholder="Choose a file"
|
placeholder="Choose a file"
|
||||||
@@ -100,8 +101,9 @@ function CreateDecorationModal(props: ModalProps) {
|
|||||||
<Forms.FormText className={Margins.top8}>
|
<Forms.FormText className={Margins.top8}>
|
||||||
File should be APNG or PNG.
|
File should be APNG or PNG.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
</Forms.FormSection>
|
</section>
|
||||||
<Forms.FormSection title="Name">
|
<section>
|
||||||
|
<Forms.FormTitle tag="h5">Name</Forms.FormTitle>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Companion Cube"
|
placeholder="Companion Cube"
|
||||||
value={name}
|
value={name}
|
||||||
@@ -110,7 +112,7 @@ function CreateDecorationModal(props: ModalProps) {
|
|||||||
<Forms.FormText className={Margins.top8}>
|
<Forms.FormText className={Margins.top8}>
|
||||||
This name will be used when referring to this decoration.
|
This name will be used when referring to this decoration.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
</Forms.FormSection>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<AvatarDecorationModalPreview
|
<AvatarDecorationModalPreview
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ function SettingsAboutComponent() {
|
|||||||
const [color2, setColor2] = useState(existingColors[1]);
|
const [color2, setColor2] = useState(existingColors[1]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Forms.FormSection>
|
<section>
|
||||||
<Forms.FormTitle tag="h3">Usage</Forms.FormTitle>
|
<Forms.FormTitle tag="h3">Usage</Forms.FormTitle>
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
After enabling this plugin, you will see custom colors in
|
After enabling this plugin, you will see custom colors in
|
||||||
@@ -191,7 +191,7 @@ function SettingsAboutComponent() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
</Forms.FormSection>);
|
</section>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
# Fix Images Quality
|
# 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({
|
export default definePlugin({
|
||||||
name: "FixImagesQuality",
|
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],
|
authors: [Devs.Nuckyz],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".handleImageLoad)",
|
find: ".handleImageLoad)",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=null;return )\i\.\i&&\(\i\|\|!\i\.isAnimated.+?:(?=\i&&\(\i="png"\))/,
|
match: /(?<=\i=)"webp"/,
|
||||||
replace: ""
|
replace: '"png"'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ function makeIcon(showCurrentGame?: boolean) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function GameActivityToggleButton() {
|
function GameActivityToggleButton(props: { nameplate?: any; }) {
|
||||||
const showCurrentGame = ShowCurrentGame.useSetting();
|
const showCurrentGame = ShowCurrentGame.useSetting();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -70,6 +70,7 @@ function GameActivityToggleButton() {
|
|||||||
role="switch"
|
role="switch"
|
||||||
aria-checked={!showCurrentGame}
|
aria-checked={!showCurrentGame}
|
||||||
redGlow={!showCurrentGame}
|
redGlow={!showCurrentGame}
|
||||||
|
plated={props?.nameplate != null}
|
||||||
onClick={() => ShowCurrentGame.updateSetting(old => !old)}
|
onClick={() => ShowCurrentGame.updateSetting(old => !old)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -97,7 +98,7 @@ export default definePlugin({
|
|||||||
find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
|
find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /className:\i\.buttons,.{0,50}children:\[/,
|
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 (
|
return (
|
||||||
<Forms.FormSection>
|
<section>
|
||||||
<Forms.FormTitle tag="h3">Filter List</Forms.FormTitle>
|
<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>
|
<Forms.FormText className={Margins.bottom8}>Comma separated list of activity IDs to filter (Useful for filtering specific RPC activities and CustomRPC</Forms.FormText>
|
||||||
<TextInput
|
<TextInput
|
||||||
@@ -140,7 +140,7 @@ function IdsListComponent(props: { setValue: (value: string) => void; }) {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="235834946571337729, 343383572805058560"
|
placeholder="235834946571337729, 343383572805058560"
|
||||||
/>
|
/>
|
||||||
</Forms.FormSection>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
// Sections header
|
// Sections header
|
||||||
{
|
{
|
||||||
find: "#{intl::FRIENDS_SECTION_ONLINE}",
|
find: "#{intl::FRIENDS_SECTION_ONLINE}),className:",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /,{id:(\i\.\i)\.PENDING,show:.+?className:(\i\.item)/,
|
match: /,{id:(\i\.\i)\.PENDING,show:.+?className:(\i\.item)/,
|
||||||
replace: (rest, relationShipTypes, className) => `,{id:${relationShipTypes}.IMPLICIT,show:true,className:${className},content:"Implicit"}${rest}`
|
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)) {
|
for (const [event, handler] of Object.entries(p.flux)) {
|
||||||
const wrappedHandler = p.flux[event] = function () {
|
const wrappedHandler = p.flux[event] = function () {
|
||||||
try {
|
try {
|
||||||
const res = handler.apply(p, arguments as any);
|
const res = handler!.apply(p, arguments as any);
|
||||||
return res instanceof Promise
|
return res instanceof Promise
|
||||||
? res.catch(e => logger.error(`${p.name}: Error while handling ${event}\n`, e))
|
? res.catch(e => logger.error(`${p.name}: Error while handling ${event}\n`, e))
|
||||||
: res;
|
: res;
|
||||||
@@ -242,7 +242,7 @@ export function unsubscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof Fl
|
|||||||
|
|
||||||
logger.debug("Unsubscribing from flux events of plugin", p.name);
|
logger.debug("Unsubscribing from flux events of plugin", p.name);
|
||||||
for (const [event, handler] of Object.entries(p.flux)) {
|
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/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { FormSwitch } from "@components/FormSwitch";
|
||||||
import { insertTextIntoChatInputBox } from "@utils/discord";
|
import { insertTextIntoChatInputBox } from "@utils/discord";
|
||||||
import {
|
import {
|
||||||
ModalContent,
|
ModalContent,
|
||||||
@@ -25,7 +26,7 @@ import {
|
|||||||
ModalRoot,
|
ModalRoot,
|
||||||
openModal,
|
openModal,
|
||||||
} from "@utils/modal";
|
} from "@utils/modal";
|
||||||
import { Button, Forms, React, Switch, TextInput } from "@webpack/common";
|
import { Button, Forms, React, TextInput } from "@webpack/common";
|
||||||
|
|
||||||
import { encrypt } from "../index";
|
import { encrypt } from "../index";
|
||||||
|
|
||||||
@@ -65,14 +66,13 @@ function EncModal(props: ModalProps) {
|
|||||||
setPassword(e);
|
setPassword(e);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Switch
|
<FormSwitch
|
||||||
|
title="Don't use a Cover"
|
||||||
value={noCover}
|
value={noCover}
|
||||||
onChange={(e: boolean) => {
|
onChange={(e: boolean) => {
|
||||||
setNoCover(e);
|
setNoCover(e);
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
Don't use a Cover
|
|
||||||
</Switch>
|
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
|||||||
@@ -21,40 +21,11 @@ import { Link } from "@components/Link";
|
|||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
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 { findByPropsLazy } from "@webpack";
|
||||||
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
|
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 {
|
interface TrackData {
|
||||||
name: string;
|
name: string;
|
||||||
album: string;
|
album: string;
|
||||||
@@ -63,16 +34,6 @@ interface TrackData {
|
|||||||
imageUrl?: string;
|
imageUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only relevant enum values
|
|
||||||
const enum ActivityType {
|
|
||||||
PLAYING = 0,
|
|
||||||
LISTENING = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
const enum ActivityFlag {
|
|
||||||
INSTANCE = 1 << 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
const enum NameFormat {
|
const enum NameFormat {
|
||||||
StatusName = "status-name",
|
StatusName = "status-name",
|
||||||
ArtistFirst = "artist-first",
|
ArtistFirst = "artist-first",
|
||||||
@@ -367,9 +328,9 @@ export default definePlugin({
|
|||||||
details: trackData.name,
|
details: trackData.name,
|
||||||
state: trackData.artist,
|
state: trackData.artist,
|
||||||
status_display_type: {
|
status_display_type: {
|
||||||
"off": 0,
|
"off": ActivityStatusDisplayType.NAME,
|
||||||
"artist": 1,
|
"artist": ActivityStatusDisplayType.STATE,
|
||||||
"track": 2
|
"track": ActivityStatusDisplayType.DETAILS
|
||||||
}[settings.store.statusDisplayType],
|
}[settings.store.statusDisplayType],
|
||||||
assets,
|
assets,
|
||||||
|
|
||||||
@@ -379,7 +340,7 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
|
|
||||||
type: settings.store.useListeningStatus ? ActivityType.LISTENING : ActivityType.PLAYING,
|
type: settings.store.useListeningStatus ? ActivityType.LISTENING : ActivityType.PLAYING,
|
||||||
flags: ActivityFlag.INSTANCE,
|
flags: ActivityFlags.INSTANCE,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,8 +19,9 @@
|
|||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
import { MessageFlags } from "@vencord/discord-types/enums";
|
||||||
import { findByPropsLazy } from "@webpack";
|
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";
|
import NoReplyMentionPlugin from "plugins/noReplyMention";
|
||||||
|
|
||||||
const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage");
|
const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage");
|
||||||
@@ -73,7 +74,7 @@ export default definePlugin({
|
|||||||
WindowStore.removeChangeListener(focusChanged);
|
WindowStore.removeChangeListener(focusChanged);
|
||||||
},
|
},
|
||||||
|
|
||||||
onMessageClick(msg: any, channel, event) {
|
onMessageClick(msg, channel, event) {
|
||||||
const isMe = msg.author.id === UserStore.getCurrentUser().id;
|
const isMe = msg.author.id === UserStore.getCurrentUser().id;
|
||||||
if (!isDeletePressed) {
|
if (!isDeletePressed) {
|
||||||
if (event.detail < 2) return;
|
if (event.detail < 2) return;
|
||||||
@@ -89,8 +90,7 @@ export default definePlugin({
|
|||||||
} else {
|
} else {
|
||||||
if (!settings.store.enableDoubleClickToReply) return;
|
if (!settings.store.enableDoubleClickToReply) return;
|
||||||
|
|
||||||
const EPHEMERAL = 64;
|
if (!MessageTypeSets.REPLYABLE.has(msg.type) || msg.hasFlag(MessageFlags.EPHEMERAL)) return;
|
||||||
if (msg.hasFlag(EPHEMERAL)) return;
|
|
||||||
|
|
||||||
const isShiftPress = event.shiftKey && !settings.store.requireModifier;
|
const isShiftPress = event.shiftKey && !settings.store.requireModifier;
|
||||||
const shouldMention = Vencord.Plugins.isPluginEnabled(NoReplyMentionPlugin.name)
|
const shouldMention = Vencord.Plugins.isPluginEnabled(NoReplyMentionPlugin.name)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { isNonNullish } from "@utils/guards";
|
|||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { Message } from "@vencord/discord-types";
|
import { Message } from "@vencord/discord-types";
|
||||||
import { findComponentByCodeLazy } from "@webpack";
|
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 FillValue = ("status-danger" | "status-warning" | "status-positive" | "text-muted");
|
||||||
type Fill = [FillValue, FillValue, FillValue];
|
type Fill = [FillValue, FillValue, FillValue];
|
||||||
@@ -48,6 +48,11 @@ export default definePlugin({
|
|||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Show milliseconds",
|
description: "Show milliseconds",
|
||||||
default: false
|
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) {
|
latencyTooltipData(message: Message) {
|
||||||
const { latency, detectDiscordKotlin, showMillis } = this.settings.store;
|
const { latency, detectDiscordKotlin, showMillis, ignoreSelf } = this.settings.store;
|
||||||
const { id, nonce } = message;
|
const { id, nonce } = message;
|
||||||
|
|
||||||
// Message wasn't received through gateway
|
// 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
|
// 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 (message.author.bot) return null;
|
||||||
|
|
||||||
|
if (ignoreSelf && message.author.id === AuthenticationStore.getId()) return null;
|
||||||
|
|
||||||
let isDiscordKotlin = false;
|
let isDiscordKotlin = false;
|
||||||
let delta = SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce); // milliseconds
|
let delta = SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce); // milliseconds
|
||||||
if (!showMillis) {
|
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