#๐งฉ-plugin-development
1 messages ยท Page 80 of 1
oh lg not a number
you can always look at the function source to figure these things out
I assure you the 10 minutes between my message and your response were spent attempting to figure these things out
it's a lot less intuitive when you're not familiar with how it all works
true sorry if that was rude
is this some discord issue or vencord issue where it doesnt add context menus to role icons when theyre expanded
i forgot which plugin added it but some did
i could
Feel like I'm going crazy. Why does this need all 3 parameters? ๐ญ
root?.render(<QuestIcon size={26} width={26} height={26} />);
just providing one or two doesn't work. needs all 3.
oh a size and either height or width works. still weird
oh nice this ruins the tooltip fun
can someone explain how im supposed to apply this to icon={QuestIcon} cause anything I try either gets me an "Oh no!" red error box or causes the tooltip to stop working specifically over the quest icon itself.
You make an anon function that returns that quest icon with that prop
Because it prefers the size parameter, set it to custom if you wanna use custom
if I just used size nothing happened
What size did you use
10 26 28 30 100 etc anything
.
.
i have no clue what that means bro ๐ญ
...root?
Set the size prop to "custom" and then you can use width and height freely
since I couldnt get the size to work with icon={QuestIcon} I tried just inserting it into the foriengObject parent which worked
it let me change its size
but the tooltip stopped working over it
Ok, well that method in that example is out anyways cause it ruins the tooltip.
ended up just doing this and it works. trying to use the svg by using findComponentByCodeLazy was a mistake
export function QuestIcon() {
return (
<svg
viewBox="0 0 24 24"
height="26"
width="26"
fill="none"
>
<path fill="currentColor" d="..." ></path>
</svg>
);
}
Alright
const root = foreignObjectChild ? createRoot(foreignObjectChild) : null;
root?.render(<QuestIcon size={26} height={26} />);
this is what I was doing cause I couldnt figure out another way to resize the component
kills the tooltip over the svg though
๐ญ
looking better tho imo. closer to the discord logo in size
why would you not just try className and changing the size with css but oh well yea just grab the svg its easier and cant just randomly break due to updates
how would I do that. anytime I tried anything other than icon={QuestIcon} it gave me the red box of doom "Oh no!" error
oh goodie I dont need the ListItemContainer component so now im only rely on one findComponentByCodeLazy call need it for the pill smh
session.defaultSession.webRequest.onHeadersReceived(
({ responseHeaders }, cb) => {
if (responseHeaders) {
const cspHeader = findHeader(
responseHeaders,
"content-security-policy"
);
if (cspHeader) delete responseHeaders[cspHeader];
const cspReportOnlyHeader = findHeader(
responseHeaders,
"content-security-policy-report-only"
);
if (cspReportOnlyHeader)
delete responseHeaders[cspReportOnlyHeader];
}
cb({ cancel: false, responseHeaders });
}
);
session.defaultSession.webRequest.onHeadersReceived = () => {};
}```
if anyone wants an easy way to remove the new csp change
replaces initCsp in csp.ts
how do I make a plugin require a restart just for turning it on or off
can only find examples for when options are changed
same, i've been wondering the same thing, all the plugins that do it don't have anything special in their define plugin or settings. not sure either
added a patch and it worked
patches: [{
find: "",
replacement: {
match: "",
replace: ""
}
}],
idk if there's any consequences to empty patches
thats what i was starting to think, thanks for confirming
i doubt it
maybe make it find something like "require_restart" as a placeholder just in case
if your plugin requires a restart and doesn't need a patch then you are doing something wrong
it's Read All Messages's fault
I need to load above it
so I use startAt: StartAt.Init,
that becomes obsolete if you enable it after startup
hmm I see
they both use addServerListElement(ServerListRenderPosition.Above, ...);
then just do patches: [] for now
lemme try again
yea patches: [] by itself doesnt force a restart
{
find: "require_restart",
replacement: {
match: "",
replace: "",
},
},
],```
i cant see this going wrong

is it fine to use empty values for now or is that dangerous?
You'll get a warning in devtools that the find was not found
iirc you can make dev tools Ignore the patch
add in noWarn: true
๐
^^
thanks
use an empty one
better than one with a find which will never match
holy banger almost have it properly detecting being on the page. just need to get it to work for navigating there manually and not by the shortcut
let's go chat we did it
hm I wonder if I can access the number of unfinished quests without needing to be on the page
QuestsStore exists
nice QuestStore.quests
<PillContainer
selected={onQuestsPage}
>
<ListItem
onClick={questButtonClicked}
className="server-list-button"
tooltip="Quests"
icon={QuestIcon}
selected={onQuestsPage}
>
</ListItem>
</PillContainer>
is there an equivalent to selected for the mini-dot (unread) or do I have to do some external handling for that
hmmm
looks like servers have unread: true but doesnt work here. sadge
my bad
where's that tutorial for connecting my account to vscode so I can test findComponentByCodeLazy without dozens of reloads
ah vencord companion
if u steal my token imma be mad
???
does cloning the github not count
yay it worked
im never gonna understand findComponentByCodeLazy lmao ๐ญ
arent you just supposed to copy a piece of code the component points you to? how is this failing smh
lazy evaluated on first access
-Lazy methods are lazy wrappers over the non lazy ones
oh I see what joona did. I hate it
non lazy ones search immediately
lazy ones only search when you actually use it
so the problem is the current find is const PillContainer = findComponentByCodeLazy("listItem,ref"); which I guess returns the first pill found? which would be the DMs button pill. problem is that one doesnt support the unread state like the guild ones do but Idk how to get one of the guild ones specifically. pain
is there a findComponentByCodeLazyButSkipTheFirstMatch

that's not how things work
if you are finding the wrong one, then you need to adjust the code you are finding it with
if listItem,ref exists in another one, choose another text
thats the only match that works ๐ญ
only way to differentiate them would be by the value of t
sigh
well then you are looking at the wrong component
look for the one that supports unread as you are saying
im looking where the components tab is placing me
theres only 1
then why it aint accepting the unread props
try a breakpoint
show an screenshot for what you are looking for
anyway on my client the only prop that seems to be being passed to ListItem is children and then the first child does have selected
when I inspect my pill I see this. when I inspect a guild pill I see this
so mine isnt taking the unread prop for some reason
i dont think its taking any of them and those are defaulting
eh
selected={onQuestsPage}
and I modify onQuestsPage based on if im on the page
the one on the left i think
im looking for the white dot
like
try this
findComponentByCodeLazy(""pill":"empty"")
hm just realized the pill isnt taking any props actually. it's the list item that is
listitem is just a wrapper for a list of pills
thus the reason it's just a div with children
the actual pill is another component, the one I sent the screenshot and the same fres is showing
your pic is hovering over the nitro button though
it uses the code of a component to find the component
and what's this
and this
ive tried that line too. but I probably didnt remove the spaces
gave it an unread prop and now it doesnt load fun
as in when discord opens the quest button isnt there anymore
oop unnested the listitem from the pill and now it's there, albeit like 40px offset down
ok nested the pill in the list item instead of the other way around. almost
lost the unread though
or it's off screen
man why is this so confusing ๐ญ
no errors 
I went back to the original nesting
cause neither of those 2 worked
just inserts a nearly empty div hmm
if I move them
<PillContainer
selected={onQuestsPage}
unread={true}
>
</PillContainer>
<ListItem
onClick={questButtonClicked}
className="server-list-button"
tooltip="Quests"
icon={QuestIcon}
selected={onQuestsPage}
unread={true}
>
</ListItem>
I end up with
oh this is the containerRef
one of these really needs to be nested in the other I think
I need wrapper inside of listitem
I need whats inside of wrapper inside of listitem
wrapper is empty
im so lost
that wrapper yes
thats a ghost wrapper
the wrapper inside of listitem isnt
agony
got rid of PillContainer entirely and it's position right now but now there's no pill when I hover or anything
the html makes it look like there should be though
when I hover a div is made and unmade
just nothing visually
ok got it working with hover and select. I had to uncomment out
const pill = containerRef?.current?.querySelector('[class*="pill"]');
const wrapper = pill?.querySelector('[class*="wrapper"]');
pill?.classList.add(wrapper ? wrapper.className : "");
unread still not working. not sure how to do that
this is why I need control of the pill instead of relying on the built in one oof
it looks like guild list items do have an unread prop on the list item itself
idk why it's not working here
on the parent anonymous wrapper whatever that is
I had that issue yesterday. gotta delete your extension cache
the what
experiment from 2021
what are the odds they just yeet the component at some point
so the only solution to have access to an unread prop is to go through html hell?
thats what v1 was before I was convinced to use components
hmmm
im aight at regex just no clue how to patch
why cant I seem to just get the icon component itself without the pill prop. whenever I try I just get Oh no!
they're siblings
but whenever I try anything from N it just dies
function QuestButton() {
const containerRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (!document.getElementById("quick-quests-extra-style")) {
const style = document.createElement("style");
style.id = "quick-quests-extra-style";
style.textContent = `
.quests-container {
position: relative;
}
.quests-container-unread-pill {
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
.quests-container-button {
position: relative;
z-index: 0;
}
`;
document.head.appendChild(style);
}
const builtinPill = containerRef?.current?.querySelector('.quests-container-button [class*="pill"]');
const builtinPillInternalWrapper = builtinPill?.querySelector('[class*="wrapper"]');
if (builtinPillInternalWrapper) {
builtinPill?.classList.add(builtinPillInternalWrapper.className);
}
}, []);
return (
<ErrorBoundary>
<div ref={containerRef} className="quests-container">
<PillItem
unread={true}
className="quests-container-unread-pill"
/>
<div className="quests-container-button">
<ListItem
onClick={questButtonClicked}
tooltip="Quests"
icon={QuestIcon}
selected={onQuestsPage}
/>
</div>
</div>
</ErrorBoundary>
);
}
now can just hookup some js to hide it when needed yippee
not really sure how React.useEffect(()) works properly so there might be a more clean way to handle that part of it
I just need everything rendered before it runs
also not sure why the built-in pill is broken when used this way
it's supposed to be .pill .wrapper but instead it's provided by the component as .pill > .wrapper
so I have to copy the wrapper class to the parent or the hover/selected effects dont work
weirdge
see react docs for that
useEffect has like 3 different uses based on what's passed to it
looked it up. seems to run one time once the component is rendered
thats one use
there are 3
hopefully the one I'm using it for works every time
I was told the other day that the :has CSS selector is laggy. Is it laggy just by existing, or only when its parent div is on the page?
don't use has
plz
if you're writing a plugin you shouldn't need it
if I cant use has then ill need to move elements around manually
sounds like xy tbh
why? if you need to change the order of elements with css, use grid or flex with the order property
can I just assign an order to each element individually?
yes
ok then yeah this is less of an issue
I'll have to call QuestsStore.getQuest a ton of times though
oh nvm I can just check for the end date div
which is what the has was for but this is easier
well not easier but easy enough and supposedly not laggy 
you will errorboundary.wrap
isnt this making it perma true though
I can make a patch rely on a dynamic variable in the rest of my plugin code?
use \i for identifiers
yeah I need it tied to a plugin setting and there being unclaimed quests so
considering patches force restarts I feel like no
same
guhhh i haven't really been following
but you cant change a patch once it's been applied
but other than that, if you can do it in js, yes
can you patch in a function then or something
it
$self.myFunc()
it would have to use some global state or smthn tho
you can call functions from your plugin
kinda is but discord is a small corpo so please understand
im forced to be hacky with it cause the ugly way discord has their components layered
do you mean I can call the function I pass to the patch from the plugin at any point during runtime? cause that would solve the issue I think?
not sure what you need, but look at other plugins that use $self to see if that would solve your issue
I need a way to toggle a component visible/invisible. I'll take a look.
or I guess with the patch it would be a way to change the prop from true to false and back dynamically
guhhhhhh too eepy to read rn 
if there are unclaimed quests and the setting for the indicator is on it would be True and otherwise False
but it sounds like that only triggers when you hover it
not dynamically
what if you set it to a variable and then update that variable in a while loop. does that work
well it might not update without hovering
might be some react shenanigans you could do
@quick zephyr not sure what you want to do, but you might want to take a look at accountPanelServerProfile
bro keeps saying not sure ๐ญ we've said it like 5 times ๐ญ will do tho
sorry, im playing tf2 and glancing at this on my second monitor
๐ญ
yeah only updates on hover rn.
I think the CSS method I'm using is gonna be the better play tbh.
this is feeling even more hacky
all the css would really be doing is hiding and unhiding. not that bad
just realized imma run into the same issue of getting the pill to rerender if I wanted to use the unread prop
ik it has something to do with state I just dont know enough about react to know what
oh got it working. idk if it's a good impl tho lol
let setHasUnclaimedQuestsState: React.Dispatch<React.SetStateAction<boolean>> = () => {};
...
function QuestButton() {
const [hasUnclaimedQuests, setHasUnclaimedQuests] = useState(false);
const containerRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
setHasUnclaimedQuestsState = setHasUnclaimedQuests;
return () => {
setHasUnclaimedQuestsState = () => {};
};
}, []);
...
start() {
setInterval(() => {
setHasUnclaimedQuestsState(prev => !prev);
}, 1000);
addServerListElement(ServerListRenderPosition.Above, this.renderQuickQuestsButton);
},
boils down to these sections
can I save state between restarts? just a boolean value that I want to keep track of? ๐ค
}).withPrivateSettings<{ interesting
image zoom does that
does it? I dont see anything in there that looks like it would
image zoom keeps whether the zoom is a square or a circle iirc
is there an easy way to add a context menu to something that usually doesn't have one ๐ค
Same way as you mod anything else
im still not at the point where I know what that means. and I was hoping there'd be a built in function like addServerListElement lol
oh I see an onContextMenu perhaps that'll work
All the existing such hooks are done by patching the corresponding part of discord
woah it worked lol
cant get middle clicking to work hmm. looks like something is eating the inputs
nice got it
function questMiddleClicked(event: React.MouseEvent<HTMLDivElement>) {
if (event.button === 1) {
event.preventDefault();
event.stopPropagation();
SettingsRouter.open("VencordPlugins");
const start = Date.now();
const waitForAddonHeaderAndSwitch = () => {
const addonHeaders = document.querySelectorAll(".vc-addon-header");
let found = false;
for (const header of addonHeaders) {
const title = header.querySelector(".vc-addon-title");
if (title && title.textContent?.trim() === "QuickQuests") {
const switchButton = header.querySelector('button[role="switch"]');
if (switchButton) {
switchButton.dispatchEvent(new MouseEvent("click", {
bubbles: true,
cancelable: true,
view: window
}));
found = true;
break;
}
}
}
if (!found) {
if (Date.now() - start < 30000) {
setTimeout(waitForAddonHeaderAndSwitch, 50);
}
}
};
waitForAddonHeaderAndSwitch();
}
}
So I have this and it's working but I would rather not have to simulate a click. I see openPluginModal and that does work, but when used externally like this the onRestartNeeded of the settings is ignored. So I'm wondering if there's some native method I'm missing or if I'm going to have to implement my own alert ๐ค
Nope, have to implement your own
agony
You can check how BetterSettings does it
you need to provide your own onrestartneeded function
doesnt help in this case I dont think?
PluginSettings uses a changes set
I cant access that
<PluginCard
onRestartNeeded={name => changes.handleChange(name)}
disabled={false}
plugin={p}
isNew={newPlugins?.includes(p.name)}
key={p.name}
/>
function openPluginModal(plugin: Plugin, onRestartNeeded?: (pluginName: string) => void): void
oh that was easy
just yoinked the onRestartNeeded there
function onRestartNeeded() {
Alerts.show({
title: "Restart required",
body: <p>You have changed settings that require a restart.</p>,
confirmText: "Restart now",
cancelText: "Later!",
onConfirm: () => location.reload()
});
}
function questMiddleClicked(event: React.MouseEvent<HTMLDivElement>) {
if (event.button === 1) {
event.preventDefault();
event.stopPropagation();
SettingsRouter.open("VencordPlugins");
openPluginModal(Plugins.QuickQuests, onRestartNeeded);
}
}
becomes this
Why the settingsrouter?
legacy from thinking opening the settings would make it work
and from the simulated click method it was needed
well thats enough for tonight. thanks for the help
Guys, can someone tell me. Where should i find patch funcs? Like usernameSpeaking. Cant understand where it comes from
That's not a function, that's a class. It's being applied conditionally when the condition on the right is met
also the patch seems to be targetting some later code
its raw minified discord code
go into devtools, then sources, thats the entirety of discord code, you find the piece of code you want to edit & thats your match
is there a built in way to verify uniqueness between different settings which all share an option pool but which need to be different from each other?
what??
worked something out
nothing like spending a few hours fumbling cause something you dismissed at the start of the debugging session was the issue
if I wanted to add an option here would that be a patch
Depends on whether that's a regular context menu
well right clicking doesnt bring it up. gotta click the dots
If you inspect it and it has role="menu" and an id, you can probably patch it via the contextMenus api
thank god was not looking forward to figuring out how to patch this
once this is done the plugin will be done woohoo
Sick
If you just search contextMenus: { in the project there's tons of examples of patching them to go off
yeah vscode's project wide search has been helping a ton
ive also just realized I made a custom implementation of openPluginModal for no reason smh
due to an oversight on my part I thought I needed to dipatch my own flux event when the modal closed
agony
all working nicely now though :)
Are you going to add a feature to only show the quests button below dms if there's a quest to do?
oh thats a good idea
well
the button requires a restart
if I want it to be read all messages compatible
otherwise it gets inserted below that
not sure if theres some way around that though
since we're both using addServerListElement(ServerListRenderPosition.Above, ...)
Can you not just return null for the component?
I assume it'd still hold its place in the list when the component changes
not quite but this worked
return (
<ErrorBoundary>
{settings.store.testSetting123 && (
neat
that was easier to add than I expected
return (
<ErrorBoundary>
{(!settings.store.onlyWhenUnclaimed || hasUnclaimedUnignoredQuests) && (
<div
made the mistake of recloning and now none of my discords will open with vencord installed. gray screens of death. fun
inject command errors now. agony
oh I can access dev tools despite it not loading
did discord update within the 5 minutes I recloned and break everything or something man
Not unless #1337479880849362994 is also broken
how do I disable a plugin from console
I don't see a stop function on the plugin object
stopPlugin doesn't take a string smh
oh I can pass it from Plugins.plugins. started again on restart though
You can remove it from the settings file
ok disabled console janitor but still won't open smh
ah
that's why
being 244 commits behind tends to cause it to fail to load. the more you know
@fallen sandal Did betterNotification's linux implemnentation support images? I couldn't get attachments or avatars working on Ubuntu 24.04 (Gnome) with notify-send 0.8.3
Sorry for bothering you here but I cant comment on the pr anymore, since I haven't contributed to vencord
Removing --hint=string:desktop-entry:discord seems to fix the issue
vs
looks like gnomes notification server doesn't support image attachments??
@dull magnet ur only gnome user ik have you seen notifications with large attachments
use ur stupid brain
probably not cause they show screenshots like that
just install gnome urself u chud
nope that's an attachment see how its different than the icon top left
Malware
give me notify-send command and ill show u how it looks
you will dbus monitor for me and tell me what it shows
nuh uuuuuhhhh
you will install gnome
I'm gonna cry
im on desktop too
i think its like notify-send -a "VEE IS DUMB APP" -h string:desktop-entry:vesktop "beep" "borp" -i file:///path/to/image.png
doesnt work
Discord's implementation seems to pass image-data directly
also try i think its like notify-send -a "VEE IS DUMB APP" -h string:desktop-entry:vesktop "beep" "borp" -h string:image-path:file:///path/to/image.png
it put's it up there as app icon
which has large previews
well larger than this
well yeah but we can't cause no support in cli
its treated as both in the spec and notif servers do their own yap
the image-path hint seems to be the most explicit ig
why do we have spec if the different servers still do whatever they want
@pearl stag replace the -i with -h string:image-path:
the app icon should still be done properly from the desktop file hint
so they can customize the look and stuff
the desktop entry doesnt work for me
it justz doesnt send the notif
oh cause ur dumb
it's Vesktop not vesktop
wait no
with Vesktop it works but no icon
with vesktop it no workie
dont make me turn on my laptop
yup that seems to work
change -a to just Vesktop
maybe its insane and taking the icon from the title
maybe it checks that the desktop-entry matches the current process
so u cant impersonate other apps
maybe

Do you mind if I commit the change?
surely it doesn't check the caller process but also parent processes
or do you want to commit it yourself
no it does cause look discord icon here
nop
just put me as the commit author
@pearl stag did you do anything special for the discord icon in that screenshot u sent
or base patch
No
OK so vee is dumb
u first
why
IT DOESNT WORK
then why did Ruostin get it fine
no the desktop entry thing needs to exist for notif settings des provide
I'll experiment
when someone asks me what @fallen sandal's age is
what number is that?
WHAT IS RHE DESKTOP ENTRY NAME U HAVE
tell me rn
if its Vesktop.desktop I'll freak
nvm now the flatpak one also broke
i think you have cursed fingers
does vesktop.desktop work
lol okay so that's why it works for the other guy
it passed a default action which makes it automatically wait
prob should report this to gnome or whoever
libnotify people then
evil virus
Hi all,
Fedora 40, Gnome 46.
How should I make a notify-send popup raise Thunderbird when clicked?
I'm experimenting with notify-send and find that its -h flag with "desktop-entry" seems to cause it not to pop up at all. I can use a basic popup with no problems, for example:
$ notify-send "This is a test" "foo"
no reply
@pearl stag do you need help with commit author stuff
I'll just commit it if you dont know how
interestingly, -a doesn't work when u pass desktop-entry hint
makes sense gnome is insane like that
assuming all sorts of yap
i mean it makes sense
what if you want a different title but still want the notif governed under the app
cope gnome user
KDE can do just fine
HUSK
@pearl stag You removed the file:// prefix
don't do that
Is it necessary? Just passing the file path seemed to work fine atleast on gnome
ah
thats not even a word
it is 
does vencord have a built in method for sanitizing strings?
sanitize for what
Since the plugin is using notify-send, if the body contains unescaped characters, some bad things can happen
Like here's an example of what a command might look like
notify-send DM Example author: helo --action=default=Open --app-name=Discord --hint=string:desktop-entry:discord -h string:image-path:...
Someone could replace the body with malicious code
what kind of malicious code
shell injection?
html?
use node apis correctly and there won't be shell injection
Dangerous:
execSync(`notify-send --foo ${userInput}`);
Safe (from shell injection - if notify-send can execute code or something, you need to escape for that):
execFile("notify-send", ["--foo", userInput]);
ah
Yeah that makes sense
syntax error ...
explode
soon
always use the methods that take an argument array, not the ones that take a single string
also execFile is the safest (and most efficient) variant since it won't launch a shell
if you need more control, you can also use spawn
@pearl stag are you gonna a write the basic html/xml parsing for the Linux notifs like you did for windows
like the generateXml() that generates toastXML?
yeah
Body text may contain markup. The markup is XML-based, and consists of a small subset of HTML along with a few additional tags. The following tags should be supported by the notification server. Though it is optional, it is recommended. Notification servers that do not support these tags should filtโฆ
Does that exist for Linux
its in a TODO in the code I pr'd
ah true
dont do the img tag though and maybe not hyperlink
actually
having "windows only" everywhere kind of sucks
what
are u manually parsing xml
probably just bold italic and hyperlink since those are the subset that the xdg portal implements
browser has apis for xml (de-)serialisation
I wonder whats the best way to implement the bold and italics
probably markdown
or more specifically * and **
but for user customization
Oh yeah
so like I send vee is dumb/ and it handles that
That should be easy*
๐ฅธ
I mean the syntax itself is passed, not the output
if that makes sense
[text](https://google.com)
Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.
instead of just text
also rounded icons on linux
Yeah definitely possible
Even discord has that by default
impossible unless discord can round images in renderer
isn't this prone to some sort of xml injection
that's just a CSS mask
it should be fine since its just given to windows' notification api
i didn't write that and probably
(please don't parse discord markdown manually)
yeah I assumed that was available
you need to pass some options
Parser.parseToAST("@fallen sandal you **stink** [fr](https://google.com)", {
channelId: message.channel_id,
messageId: message.id,
allowLinks: true,
allowHeading: true,
allowList: true,
allowEmojiLinks: true,
viewingChannelId: SelectedChannelStore.getChannelId(),
})
full list
return {
channelId: t,
messageId: n,
allowLinks: !!r.allowLinks,
allowDevLinks: !!r.allowDevLinks,
formatInline: !!r.formatInline,
noStyleAndInteraction: !!r.noStyleAndInteraction,
allowHeading: !!r.allowHeading,
allowList: !!r.allowList,
previewLinkTarget: !!r.previewLinkTarget,
disableAnimatedEmoji: !!r.disableAnimatedEmoji,
isInteracting: !!r.isInteracting,
allowEmojiLinks: !1,
disableAutoBlockNewlines: !0,
mentionChannels: [],
soundboardSounds: [],
muted: !1,
unknownUserMentionPlaceholder: false && !0,
viewingChannelId: r.viewingChannelId,
forceWhite: !!r.forceWhite
}
@dull magnet @dull magnet @dull magnet
blehg
nevermind it isn't
async function cropImageToCircle(src, size = 128) {
const { promise: imgLoaded, resolve } = Promise.withResolvers();
const img = new Image();
img.onload = resolve;
img.crossOrigin = "anonymous";
img.src = src;
await imgLoaded;
const canvas = document.createElement("canvas");
canvas.width = canvas.height = size;
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
ctx.clip();
ctx.drawImage(img, 0, 0, size, size);
return canvas.toDataURL();
}
await cropImageToCircle("https://cdn.discordapp.com/avatars/721853658941227088/bd1051c099643c15a5049506324a01b6.png?size=128")
canvas is super powerful
u could also do squircle
๐ฅ
EW
Where does Parser come from? The Parser from @webpack/common doesnt seem to have parseToAST
it does
does it
the type is missing parseToAst cause i was too lazy to type it
ah
put nin0 on it (he will chatgpt it out)
i love this
inverse squircle jumpscare
SIZE OF THE CANVAS, IN PIXELS
lmao how did u do that
n < 1
I think with the proposed superellipse border radius feature in css you'll be able to use custom parameters too
@fallen sandal
omg baby cait hiii
is it just me or is that circle not smooth
just u
looks alright
no
the first one was drawn with gpu, second one with software
they produce slightly different results it's funny
one is slightly more jagged?? but how does the component that drew it make a difference
also the user will definitely never see a difference ๐ญ
shes so small...
gpu is using physical components to do it
so itll depend on how the manufacturer implemented whatever its using to draw the circle
ngl my intuition tells me the edge drawing algorithm would be exactly the same but I know nothing about hardware so idk ๐ญ
i couldnt tell a difference before
i used a different clipping method (evenodd instead of nonzero) but it's the same
can u tell a difference now
hi i think my internet is lagging
how did u tell it to use gpu vs software
that's literally the same file ๐ญ
const useSoftwareRendering = true;
canvas.getContext("2d", { willReadFrequently: useSoftwareRendering });
ya
not the same file but have the same contents
insane thatd youd ever want to force software
if you very frequently need to read the data, it's more expensive to keep copying the data
@dull magnet youre expensive
index.tsx: Lines 758-760
const ctx = canvas.getContext("2d", {
willReadFrequently: true
})!;
Is that ungoogled chromium?
ye
So I think I just found a bug with the Experiments plugin. If you have the Experiments plugin enabled then after a restart all gradient names are permanently flattened until you disable the plugin. I thought it was something wrong with my PR for showMeYourName so I spent ages reinjecting over and over to fix it. But it never worked so I tested @hushed loom and @humble tulip 's PRs and same issue. Then I started disabling plugins in batches til I found the Experiments plugin causing it. I just had a friend test it with a normie install and he confirm it happened to him. I also tested it as the only enabled plugin and still happened.
tldr; If you enable the Experiments plugin all gradient names are perma flattened until you disable the plugin or reinject Vencord, but then it will happen again after the first restart.
Took a look at the plugin code and have no idea what would be causing it. ๐ค
Have you checked if it's an experiment you have enabled/disabled and not the plugin itself?
They work for me and I've had the experiments plugin enabled forever 
wtf
oh if I close discord fully and start it again it's fixed
it only breaks after a ctrl+r reload or a reload induced by enabling the plugin
can you try that ^
Works fine after ctrl+r, reload from disabling/enabling the plugin and full restart
It's kinda hard to tell because the gradients in this server are so subtle but it's the only server I'm in that has them
Like that after reload I genuinely can't tell lol
and thats with experiments enabled? man this is weird. why would it be effecting me and my friend but not you
Yeah, I've had experiments on the whole time, aside from disabling it to test reenabling it
I'm on my pr branch atm
yeah checked out to your pr
disabled every plugin except showmeyourname and experiments
rebuilt
reinjected
started up > had gradients
ctrl + r > gradients gone
quit discord and launch it again > gradients back
Beats me
I'll disable stuff later and see if I have something enabled that fixes it
yeah thats the only thing I can think of, some extra plugin that undoes the damage or smthn ๐ค
I deleted my settings file and restarted, then just enabled the experiments plugin and smyn and they're still fine

every time I think of something and go "oh maybe that's it" I remember my friend who just has it installed the regular way had the same issue hmmmm this sucks
ok narrowed it even further
it only effects the server I ctrl+r in
if I reload in another server then nav to my server with gradients they're there
heyo
im making a plugin to make searching for messages easier
idk how to use native discord components tho
so idk how to render messages like how discord does
I hope these types are accurate enough for the parser result
The link title may be [text](https://example.com "this is a title") text
Determines what the tooltip says on hover
ah
I didnt even know that you could specify a tooltip lol
I havent gotten hyperlinks to work yet tho
probably missing a parser option
with ts parser.parseToAST(notificationBody, { channelId: basicNotification.channel_id, messageId: basicNotification.message_id, allowLinks: true, allowDevLinks: true, allowHeading: true, allowList: true, allowEmojiLinks: true, previewLinkTarget: true, viewingChannelId: basicNotification.channel_id, });
Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
this is what hell looks like
The autolink rule seems to be catching it before the link rule so it's not reaching it
I dunno why though
brother what...???!? over an hour of debugging just to figure out
try {
if (isRepliedMessage && !replies) {
return <>{mentionSymbol}{nick || display || username}</>;
}
needs to be
if (isRepliedMessage && !replies) {
return <>{mentionSymbol}{nick || display || username}</>;
}
try {
how does that possibly cause an error .-.
Is there a hook call below that block?
would wrapEmojis count
const wrapEmojis = findByCodeLazy(/"span",\{className:\i\.emoji,children:/);
I call this a few times in the try block
nothing else looks hooky to me
Yeah I think they do use a hook in there
I had to move the wrap emoji calls to before my ifs as that broke rule of hooks too
You can pass undefined into it and it's fine so moving it up shouldn't affect anything
well it's not an issue anymore
after moving that if statement up
no clue why
๐ค
oh nvm
so what I need that call above what
all of my early returns?
agony
Yeah just put it in a place where it'll run on every function call
Feels wasteful if you know you don't need it but react is react 
my issue is all the code ill useless be running each time
and gonna have to add a bunch of ?.xyz
React 
ok not breaking the plugin anymore but still getting Rendered more hooks than during the previous render. in console. hm
moved the wrapemojis calls out of the try except and it stopped
now I practically have nothing in the try except block anyways
I might as well get rid of it at this point
.-.
do I just let error boundary take the wheel
Do not put hooks, or functions that call hooks, inside conditionals or loops
I didnt consider try except a conditional
Do not use try/catch either, use errorboundary
try/catch doesn't work in react components
I see
I am using error boundary
I just thought try except was good too
hm it's not causing the plugin any issues anymore but console still filled with Rendered more hooks than during the previous render. from WebpackModule ๐ค
well by filled with I mean it posted 4 at startup and then no more
Do you have code
hm the issue is the wrapEmojis function itself
removing it stops them from popping up into console
const firstValue = wrapEmojis(values[first].value);
const secondValue = second ? wrapEmojis(values[second].value) : null;
const thirdValue = third ? wrapEmojis(values[third].value) : null;
oh do those count as conditionals
sigh
Hey look, wrapEmojis inside conditionals
TIL react is ass
Turns out the second parameter is not options, the third one is
Easiest way around it is to wrap each of those wrapEmojis calls in a separate component
If it isn't one already, have you checked that
suffocate says wrapemojis can take null
so imma just do that
well ""
const firstValue = wrapEmojis(values[first].value);
const secondValue = wrapEmojis(second ? values[second].value : "");
const thirdValue = wrapEmojis(third ? values[third].value : "");
oh
what's the second second param then?
Second param is boolean inline
ah
yeah works

thanks
now to figre out why reloading with experiments enabled breaks gradients
anyone else with access to a server that has gradient roles willing to test?
If it works when you navigate away and back, surely that just means that either it's not rerendering when the gradient colours are fetched or it's not fetching them right away?
no
when I navigate away and back it still is flat
when I scroll up in that channel all new messages are still flat
when I switch channels in that guild all messages are still flat
if I switch servers that new server is normal
Oh, I assumed from this moving away and back would do it
if I go back the issue is still there on the original server I reloaded on
Hmm
also I checked the response of the smyn plugin and it responds with the gradient colors. the problem is it relies on the parent have the correct classes and the correct injected css that discord does on its own
Even if I disable the enhanced role colours experiment and reenable it it just puts the gradients back in realtime with no issues
really need another person or two to test cause I'd be fully willing to believe it's just my setup being weird but the fact my friend had the same issue confuses me
the difference in the html discord generates
We're gonna do all this and find out you two have some other experiment (enabled legitimately on discord's side) that breaks it
Although the experiments plugin affecting it is weird in itself
{
find: 'type:"user",revision',
replacement: {
match: /!(\i)&&"CONNECTION_OPEN".+?;/g,
replace: "$1=!0;"
}
},
This patch is it though if my testing isnt gaslighting me
I comment this one out and it stops happening

is anyone here on linux?
could you try notify-send "title" "Example <b>bold text</b>" and see if the bold tag is actually processed properly
just wondering if the common ones support it
this isn't the case on ubuntu 24.04 with gnome
(unless i'm missing something)
Pretty sure the sender needs to set a flag that it contains markup
Dunno if notify-send sets that by default
gnome might simply be insane
they work on KDE
also @pearl stag I sent another patch
For some reason I dont seem to be getting emails from the repo lol I wasnt watching the repo
thanks for the contribution
I wonder if notify-send has a way to identify which features are available
I couldn't find anything in the man pages
ugh
I'll just make the setting false by default, and add a warning to the description
you can check $XDG_CURRENT_DESKTOP
Wouldn't I also need to know which environments support the markup?
can u try notify-send "title" "Example <b>bold text</b>" --hint=boolean:use-body-markup:true
still renders the tags as text
does \n work
seems to just be removed
hm so what im reading is correct then
notify-send "title" "Newline -> \n here"
there is a flag to enable markup like that but i cant find how to pass it
can you link the resource you were looking at?
Read-only mirror of https://gitlab.gnome.org/GNOME/gnome-shell - GNOME/gnome-shell
thanks
https://github.com/GNOME/gnome-shell/blob/f480f18c7e38449fac2798199fcb849ddf5c7bad/js/ui/notificationDaemon.js#L205 it looks like its enabled by default??
notificationDaemon.js: Line 205
useBodyMarkup: true,
Maybe ubuntu's default notification server just sucks lol
it should be the same
I can't really see another reason why markup would work in one de but not in gnome
cause they are different code bases
and the markup should be working
i think this may be a bug
notificationDaemon.js: Lines 271-278
GetCapabilities() {
return [
'actions',
// 'action-icons',
'body',
// 'body-hyperlinks',
// 'body-images',
'body-markup',
@dull magnet you gnome user can u notify-send "title" "Example <b>bold text</b>"
nope
I'm a baby cookie where is mama
horrible choice to open plugin chat rn
@pearl stag I see you're manually parsing the ast to html but you can just have simplemarkdown do it if you want
const SimpleMarkdown = findByProps("htmlFor");
SimpleMarkdown.defaultHtmlOutput(SimpleMarkdown.defaultInlineParse('This is **markdown** parsed *to html* via __SimpleMarkdown__'));``` = ```html
This is <strong>markdown</strong> parsed <em>to html</em> via <u>SimpleMarkdown</u>
Didn't know this existed, thanks!
I'll definitely switch to that lol
np 
I did try using Discord's list of rules for it at first but they don't provide html render methods for a lot and it makes it messy, so I just did simplemarkdown's defaults
Looks like it has a sanitize text function too, might be useful since you asked about that earlier I think
is there any way to define a plugin setting asynchronously like based on a value fetched from native
define?
wdym define
do you mean default value
the options n stuff
like a select option?
yeag
what's this for why does it have to be async
async cause functions n stuff coming out of native is async
elaborate a bit cause the answer depends
trying to make betterNotifications options more dynamic
i need to get XDG_CURRENT_DESKTOP into renderer
just an env variable?
should be
ah right, plugin ipc doesnt support sync ipc
you need to make your own component
OptionType.Component
then inside the component useAwaiter() on the promise
just had another friend who just has a normal install test it and they also experienced experiments killing the gradient ๐ญ why are you immune

woot pretty much done just some cleaning and refactoring now I think :)
what is is about?
@steady cedar plugin im making for quest stuff
:)
oh cool
currently changing how im doing the styling.
What is the difference between it and the quests tab on discovery?
does applying classes based on conditions count as "No raw DOM manipulation" under the plugin rules
depends
if you're conditionally applying classes to your own components in markup that's totally fine and even good, vencord does that too
if you're using dom methods to change classes dynamically that's bad
im applying classes to the quest tiles based on the current state of the settings. so should I instead be manually rendering the quests grid ๐ค
or I guess patch the individual tile? ๐ค
how are u applying the classes
every time I navigate to the quests page or receive a flux event which would update the values I reprocess the divs
show code
async function processQuestData() {
const startTime = Date.now();
const timeout = 30000;
if (onQuestsPage) {
while (Date.now() - startTime < timeout && !cancelQuestProcessing) {
const questTiles = document.querySelectorAll("[id^='quest-tile-']");
if (questTiles.length > 0) {
break;
}
await new Promise(resolve => setTimeout(resolve, 50));
if (cancelQuestProcessing) {
return;
}
}
const questTiles = document.querySelectorAll("[id^='quest-tile-']");
questTiles.forEach(tile => { tile.classList.remove("vc-qq-quest-item-reorder-unclaimed", "vc-qq-quest-item-reorder-ignored", "vc-qq-quest-item-restyle-unclaimed", "vc-qq-quest-item-restyle-ignored"); });
}
let totalQuests = 0;
let unclaimedUnignoredQuests = 0;
const { quests } = QuestStore;
const { ignoredQuests } = settings.store;
const ignoredQuestsSet = new Set(ignoredQuests.split("\n").map(line => normalizeQuestName(line)));
const cleanedIgnoredQuests: string[] = [];
for (const quest of quests.values()) {
const questName = normalizeQuestName(quest.config?.messages?.questName || "");
const tile = document.getElementById(`quest-tile-${quest.id}`);
totalQuests++;
const claimedQuest = quest.userStatus?.claimedAt;
const questExpired = new Date(quest.config.expiresAt) < new Date();
const questIgnored = ignoredQuestsSet.has(questName);
if (!claimedQuest && !questExpired) {
unclaimedUnignoredQuests++;
if (!questIgnored) {
tile && settings.store.restyleUnclaimed ? tile.classList?.add("vc-qq-quest-item-restyle-unclaimed") : null;
tile && settings.store.reorderQuests ? tile.classList?.add("vc-qq-quest-item-reorder-unclaimed") : null;
}
}
if (claimedQuest && !questIgnored) {
tile && settings.store.restyleClaimed ? tile.classList?.add("vc-qq-quest-item-restyle-claimed") : null;
tile && settings.store.reorderQuests ? tile.classList?.add("vc-qq-quest-item-reorder-claimed") : null;
}
if (questIgnored) {
if (!questExpired && !claimedQuest) {
cleanedIgnoredQuests.push(questName);
tile && settings.store.restyleIgnored ? tile.classList?.add("vc-qq-quest-item-restyle-ignored") : null;
tile && settings.store.reorderQuests ? tile.classList?.add("vc-qq-quest-item-reorder-ignored") : null;
}
continue;
}
}
console.info(`[QuickQuests]: Found ${totalQuests} total quests.`);
console.info(`[QuickQuests]: Found ${unclaimedUnignoredQuests} unclaimed unignored quests.`);
updateHasUnclaimedUnignoredQuests(unclaimedUnignoredQuests > 0);
overwriteIgnoredQuests(cleanedIgnoredQuests.join("\n"));
}
right that'll be fun
it's not hard
the component code will have className: stuff
just patch it to be
className: stuff + $self.getExtraClasses()
getExtraClasses just returns foo bar baz to add those 3 classes
way easier than what you're doing
you can even use react hooks to dynamically update based on store data changing
can I effect the inline style that way ๐ค
ok so that will let me make the tile colors a setting like I originally wanted to do
index.ts: Lines 38-66
// add --avatar-url-<resolution> css variable to avatar img elements
// popout profiles
{
find: "#{intl::LABEL_WITH_ONLINE_STATUS}",
replacement: {
match: /src:null!=\i\?(\i).{1,50}"aria-hidden":!0/,
replace: "$&,style:$self.getAvatarStyles($1)"
}
},
// chat avatars
{
find: "showCommunicationDisabledStyles",
replacement: {
match: /src:(\i),"aria-hidden":!0/,
replace: "$&,style:$self.getAvatarStyles($1)"
}
}
],
getAvatarStyles(src: string | null) {
if (!src || src.startsWith("data:")) return {};
return Object.fromEntries(
[128, 256, 512, 1024, 2048, 4096].map(size => [
`--avatar-url-${size}`,
`url(${src.replace(/\d+$/, String(size))})`
])
);
},
nice
while I have your attention can you try out that experiments bug I mentioned earlier to see if it effects you or not. cause it's effecting me and a few friends
I'm in bed
f
haha yeah not hard... 
ok got inserting the class to the main tile to work. if I want to allow custom colors though I'll need to insert styles on 7 child elements which means 7 patches... maybe ill not implement that after all. can cope with the default colors I pick.
well I have a good bit of refactoring to do to dedupe some logic but the patch is working so it's mostly done \0w0/
horror
o/ (smol repost since this the better channel for it)
I'm working on a Plugin that needs to do http reqeusts on arbitrary urls, so I'm trying to edit the CspPolicies map, my current code is this for native.ts
import { logger } from './index';
import { CspPolicies, ConnectSrc } from "@main/csp";
export function addCsp(url: string) {
CspPolicies[`${url}`] ??= ConnectSrc;
logger.info(`Added CSP for ${url}`, CspPolicies[`${url}`]);
}
But for some reason any CspPolicies edit (I'm doing it on plugin load) seems to break Discord completely on launch, no window gets created as instead I just get the JS error below...
Is the code you showed in native.ts?
yup the whole code pasted is my native.ts file
Im guessing logger is in index.ts(x)?
I dont think you can import things from the frontend in native.ts
the code gets run in different processes so you cant import them like that
you can use VencordNative.csp.requestAddOverride
oh so imports were breaking it?
yeah
directly in index.tsx?
or still native
ye index
ah tysm <3
How can I find a specific export based on its properties? I am trying to access the appearance/developerMode discord settings option and read its value
const DeveloperMode = getUserSettingLazy("appearance", "developerMode")!;
thank you!
normally it'd just be find(m => m.foo === "bar") but in this case it's an XY problem, Joona gave you the solution
vp UserSettingsAPI
Couldn't find a plugin with that name, and there are no plugins with similar names.
how does getting around the csp policies work?
my websocket doesnt connect anymore :/
i figured it out, i wasnt restarting fully
the quests code is funky. no matter where I tried overwriting styles it only ever accepted the default styles. like if it default had a "color" style, it would only allow overwriting that, not adding any new ones. ๐ค
can you not use css?
that's what I'm already doing. I injected classes which works just fine. but I wanted to allow the user to set a custom highlight color rather than the default ones I pick out and that would require inlining styles
not a huge deal cause you're not on the quest page often anyways but just something extra I wanted to try
unless you can use the plugin settings in the css file lol
why not use different classes for the different types
thats... what I said im already doing?
a class for unclaimed, a class for claimed, a class for ignored
but that still requires me to hard code the colors for them
You could use a css var and set the value on a further up parent or root, I dunno how cursed that'd be though
I think I just figured it out. not sure if it'll work on all the elements ill need to inline but I got one to work
i think you can also set the css variables on inline styles, and use them in classes for the same element
Yeah that's kinda what I meant
Set the colour somewhere you can access via code easily and do the rest with css
it does, thats the c in css
so I just style the main container with variables from the plugin settings and then the css file can take it from there interesting
also you can do this
thats certainly better than 7ish patches
thats what my question was about "if it included children"
if you add variables to an inline style block will those variables work in the inline styles on its children
looks like it does
neat
Yeah you could add them to the html tag and they should still work all the way down provided they don't get overwritten
{
find: "id:\"quest-tile-\".concat",
replacement: {
match: /(concat\((\i)[^}]+},)className:(\i\(\)\([^)]+\)),/,
replace: "$1className:$self.getQuestTileClasses($3, $2),style:$self.getCustomColorVars(),"
}
},
nice
based
Not to make it even more complex, but ClientTheme has a colour picker component you could use if you wanted as well
oh that would be much better than having to self validate the input and having no preview
will do that after I finish this bit. have to ensure the text doesnt become unreadable no matter the color picked.
Good luck lol, auto text contrast for custom themes sucks
Make the user pick the text colour too lol
hmmm the inline style set by the component is overriding my css
nvm suddenly working. not sure why it had a change of heart
Yeah I think inline styles have a higher specificity than css, not sure if !important overrides it though
I'm no css wizard
yay or nay? Im wondering if I should slightly blur the foreground image's edges
๐ค hmmm think this is gonna be the best imma be able to do with it.
really annoying you apparently cant change an svg's fill with css so I have to hide the watch again icon or patch it 
or to correct myself, you can with css, but svgs ignore variables declared above them
and I kinda need it to follow the variables if I want it visible lol
Have you tried setting color instead of fill?
I was mistaken, the actual issue was that they dont support variables above them not that the css doesnt work
oh nvm teehee
i think I just typod the variable name
5am moment
the correct css was fill though
.vc-qq-quest-item> :nth-child(2) button>div>div>svg path {
fill: var(--vc-qq-quest-name);
}
