mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-02-24 07:25:10 +00:00
CustomVoiceFilter: Refactor WikiHomeModal with improved structure and Markdown support
This commit is contained in:
parent
fe417ace18
commit
104729558f
4 changed files with 208 additions and 47 deletions
107
src/plugins/customVoiceFilter/Markdown.tsx
Normal file
107
src/plugins/customVoiceFilter/Markdown.tsx
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { findByCode, findByProps, findByPropsLazy } from "@webpack";
|
||||
import { Parser } from "@webpack/common";
|
||||
|
||||
import { openCreateVoiceModal } from "./CreateVoiceFilterModal";
|
||||
import { openHelpModal } from "./HelpModal";
|
||||
import { openVoiceFiltersModal } from "./VoiceFiltersModal";
|
||||
|
||||
interface MarkdownRules {
|
||||
allowDevLinks: boolean;
|
||||
allowEmojiLinks: boolean;
|
||||
allowHeading: boolean;
|
||||
allowLinks: boolean;
|
||||
allowList: boolean;
|
||||
channelId: string;
|
||||
disableAnimatedEmoji: boolean;
|
||||
disableAutoBlockNewlines: boolean;
|
||||
forceWhite: boolean;
|
||||
formatInline: boolean;
|
||||
isInteracting: boolean;
|
||||
mentionChannels: string[];
|
||||
messageId: string;
|
||||
muted: boolean;
|
||||
noStyleAndInteraction: boolean;
|
||||
previewLinkTarget: boolean;
|
||||
soundboardSounds: string[];
|
||||
unknownUserMentionPlaceholder: boolean;
|
||||
viewingChannelId: string;
|
||||
}
|
||||
|
||||
const defaultRules: Partial<MarkdownRules> = { allowLinks: true, allowList: true, allowHeading: true };
|
||||
|
||||
const MarkdownContainerClasses = findByPropsLazy("markup", "codeContainer");
|
||||
const modalLinkRegex = /^<vf:(help|createVoice|main)>/;
|
||||
const imageRegex = /^!\[((?:\[[^\]]*\]|[^[\]]|\](?=[^[]*\]))*)\]\(\s*((?:\([^)]*\)|[^\s\\]|\\.)*?)\)/;
|
||||
|
||||
const modals: Record<string, { action: () => string, name: string; }> = {
|
||||
help: {
|
||||
action: openHelpModal,
|
||||
name: "Help menu"
|
||||
},
|
||||
createVoice: {
|
||||
action: () => openCreateVoiceModal(),
|
||||
name: "Voice pack creator menu"
|
||||
},
|
||||
main: {
|
||||
action: openVoiceFiltersModal,
|
||||
name: "Main menu"
|
||||
}
|
||||
};
|
||||
|
||||
const parser: typeof Parser.parse = proxyLazy(() => {
|
||||
const DiscordRules = findByProps("AUTO_MODERATION_SYSTEM_MESSAGE_RULES").RULES;
|
||||
const AdvancedRules = findByCode("channelMention:")({});
|
||||
|
||||
const customRules = {
|
||||
modalLink: {
|
||||
order: DiscordRules.staticRouteLink,
|
||||
match: source => modalLinkRegex.exec(source),
|
||||
parse: ([, target]) => (modals[target]),
|
||||
react: ({ action, name }) => (
|
||||
<span className="channelMention interactive vc-voice-filters-modal-link" role="link" onClick={action}>{name}</span>
|
||||
),
|
||||
requiredFirstCharacters: ["<"]
|
||||
},
|
||||
image: {
|
||||
...Parser.defaultRules.link,
|
||||
match: source => imageRegex.exec(source),
|
||||
parse: ([, title, target]) => ({ title, target }),
|
||||
react: ({ title, target }) => <div className="vc-voice-filters-md-image">
|
||||
<img src={target} alt={title} />
|
||||
</div>,
|
||||
requiredFirstCharacters: ["!"]
|
||||
}
|
||||
};
|
||||
|
||||
const builtinRules = new Set([...Object.keys(DiscordRules), ...Object.keys(AdvancedRules)]);
|
||||
|
||||
for (const rule of builtinRules) {
|
||||
customRules[rule] = {
|
||||
...DiscordRules[rule],
|
||||
...AdvancedRules[rule],
|
||||
};
|
||||
}
|
||||
|
||||
console.log(customRules);
|
||||
|
||||
return (Parser as any).reactParserFor(customRules);
|
||||
});
|
||||
|
||||
interface MarkdownProps {
|
||||
content: string;
|
||||
markdownRules?: Partial<MarkdownRules>;
|
||||
}
|
||||
|
||||
|
||||
export function Markdown({ content, markdownRules = defaultRules }: MarkdownProps) {
|
||||
return <div className={`${MarkdownContainerClasses.markup} vc-voice-filters-md`}>
|
||||
{parser(content, false, markdownRules)}
|
||||
</div>;
|
||||
}
|
|
@ -5,11 +5,47 @@
|
|||
*/
|
||||
|
||||
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { Button, Card, Forms, Text, useState } from "@webpack/common";
|
||||
import { Button, Card, Flex, Forms, Text, useState } from "@webpack/common";
|
||||
|
||||
import { openCreateVoiceModal } from "./CreateVoiceFilterModal";
|
||||
import { openHelpModal } from "./HelpModal";
|
||||
import { openVoiceFiltersModal } from "./VoiceFiltersModal";
|
||||
import { Markdown } from "./Markdown";
|
||||
|
||||
interface Section {
|
||||
title: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
const sections: Section[] = [
|
||||
{
|
||||
title: "How to install a voicepack",
|
||||
content: "To install a voicepack, you need to paste the voicepack url in the <vf:main>"
|
||||
},
|
||||
{
|
||||
title: "How to create a voicepack",
|
||||
content: `You have two methods to create a voicepack:
|
||||
1. Use the <vf:createVoice> (recommended)
|
||||
2. Use the <vf:help> (advanced)`
|
||||
},
|
||||
{
|
||||
title: "How does it work?",
|
||||
content: `Discord actually uses a Python project named [Retrieval-based Voice Conversion](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion) to convert your voice into the voice model you picked.
|
||||
This voice cloning technology allows an audio input to be converted into a different voice, with a high degree of accuracy.
|
||||
Actually, Discord uses ONNX files to run the model, for a better performance and less CPU usage.
|
||||
`
|
||||
},
|
||||
{
|
||||
title: "How to create an ONNX from an existing RVC model?",
|
||||
content: `RVC models can be converted to ONNX files using the [W-Okada Software](https://github.com/w-okada/voice-changer/).
|
||||
MMVCServerSio is software that is issued from W-Okada Software, and can be downloaded [here](https://huggingface.co/datasets/Derur/all-portable-ai-in-one-url/blob/main/HZ/MMVCServerSIO.7z).
|
||||
Thats the actual software that does exports RVC models to ONNX files.
|
||||
Just load your model inside MMVCServerSio, and click on "Export ONNX":
|
||||

|
||||
Enjoy you now have a ONNX model file for your voicepack!`
|
||||
},
|
||||
{
|
||||
title: "How to train my own voice model?",
|
||||
content: "Refers to [this video](https://www.youtube.com/watch?v=tnfqIQ11Qek&ab_channel=AISearch) and convert it to ONNX."
|
||||
}
|
||||
];
|
||||
|
||||
interface WikiHomeModalProps {
|
||||
modalProps: ModalProps;
|
||||
|
@ -19,51 +55,21 @@ interface WikiHomeModalProps {
|
|||
|
||||
export function WikiHomeModal({ modalProps, close, accept }: WikiHomeModalProps) {
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE} className="vc-voice-filters-wiki">
|
||||
<ModalHeader>
|
||||
<Forms.FormTitle tag="h2" className="modalTitle">
|
||||
Wiki Home
|
||||
</Forms.FormTitle>
|
||||
<ModalCloseButton onClick={close} />
|
||||
</ModalHeader>
|
||||
<ModalContent>
|
||||
<br /><br />
|
||||
<ModalContent style={{ paddingBlock: "0.5rem" }}>
|
||||
<Flex style={{ gap: "0.5rem" }} direction={Flex.Direction.VERTICAL}>
|
||||
<Text>Here are some tutorials and guides about the Custom Voice Filter Plugin:</Text>
|
||||
<br /><br />
|
||||
<CollapsibleCard title="How to install a voicepack" content={
|
||||
<Text>To install a voicepack, you need to paste the voicepack url in the <a onClick={() => openVoiceFiltersModal()}>main menu</a>.</Text>
|
||||
} /><br />
|
||||
<CollapsibleCard title="How to create a voicepack" content={
|
||||
<>
|
||||
<Text>You have two methods to create a voicepack:</Text><br />
|
||||
<Text>1. Use the <a onClick={() => openCreateVoiceModal()}>voicepack creator modal</a> (recommended)</Text><br />
|
||||
<Text>2. Use the <a onClick={() => openHelpModal()}>Help Modal</a> (advanced)</Text>
|
||||
</>
|
||||
} /><br />
|
||||
<CollapsibleCard title="How does it work?" content={
|
||||
<>
|
||||
<Text>Discord actually uses a Python project named <a href="https://github.com/RVC-Project/Retrieval-based-Voice-Conversion">Retrieval-based Voice Conversion</a><br />to convert your voice into the voice model you picked.</Text><br />
|
||||
<Text>This voice cloning technology allows an audio input to be converted into a different voice, with a high degree of accuracy.</Text><br />
|
||||
<Text>Actually, Discord uses ONNX files to run the model, for a better performance and less CPU usage.</Text><br />
|
||||
<img style={{ width: "100%" }} src="https://fox3000foxy.com/voicepacks/assets/working.png" alt="" />
|
||||
</>
|
||||
} /><br />
|
||||
<CollapsibleCard title="How to create an ONNX from an existing RVC model?" content={
|
||||
<>
|
||||
<Text>RVC models can be converted to ONNX files using the <a href="https://github.com/w-okada/voice-changer/">W-Okada Software</a>.</Text><br />
|
||||
<Text>MMVCServerSio is software that is issued from W-Okada Software, and can be downloaded <a href="https://huggingface.co/datasets/Derur/all-portable-ai-in-one-url/blob/main/HZ/MMVCServerSIO.7z">here</a>.</Text><br />
|
||||
<Text>Thats the actual software that does exports RVC models to ONNX files.</Text><br />
|
||||
<Text>Just load your model inside MMVCServerSio, and click on "Export ONNX":</Text><br />
|
||||
<img src="https://fox3000foxy.com/voicepacks/assets/export-1.png" alt="" /><br />
|
||||
<img src="https://fox3000foxy.com/voicepacks/assets/export-2.png" alt="" /><br />
|
||||
<Text>Enjoy you now have a ONNX model file for your voicepack!</Text>
|
||||
</>
|
||||
} /><br />
|
||||
<CollapsibleCard title="How to train my own voice model?" content={
|
||||
<>
|
||||
<Text>Refers to <a href="https://www.youtube.com/watch?v=tnfqIQ11Qek&ab_channel=AISearch">this video</a> and convert it to ONNX.</Text>
|
||||
</>
|
||||
} /><br />
|
||||
|
||||
{sections.map((section, index) => (
|
||||
<CollapsibleCard key={index} title={section.title} content={section.content} />
|
||||
))}
|
||||
</Flex>
|
||||
</ModalContent>
|
||||
<ModalFooter>
|
||||
<Button onClick={close}>Close</Button>
|
||||
|
@ -86,15 +92,20 @@ export function openWikiHomeModal(): string {
|
|||
return key;
|
||||
}
|
||||
|
||||
function CollapsibleCard({ title, content }: { title: string; content: React.ReactNode; }) {
|
||||
interface CollapsibleCardProps {
|
||||
title: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
function CollapsibleCard({ title, content }: CollapsibleCardProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Card style={{ background: "var(--background-secondary)", width: "100%" }}>
|
||||
<Card onClick={() => setIsOpen(!isOpen)} style={{ cursor: "pointer", background: "var(--background-primary)", padding: "10px", marginBottom: isOpen ? "10px" : "0px" }}>
|
||||
<Card className="vc-voice-filters-card">
|
||||
<Card onClick={() => setIsOpen(!isOpen)} style={{ cursor: "pointer", background: "var(--background-primary)", padding: "10px" }}>
|
||||
<Text style={{ fontSize: "18px", fontWeight: "bold" }}>{title}</Text>
|
||||
</Card>
|
||||
{isOpen && <Text style={{ padding: "10px", paddingTop: "0px" }}>{content}</Text>}
|
||||
{isOpen && <Markdown content={content} />}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
*/
|
||||
|
||||
// Imports
|
||||
import "./style.css";
|
||||
|
||||
import { DataStore } from "@api/index";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
|
|
41
src/plugins/customVoiceFilter/style.css
Normal file
41
src/plugins/customVoiceFilter/style.css
Normal file
|
@ -0,0 +1,41 @@
|
|||
.vc-voice-filters-wiki {
|
||||
max-width: var(--modal-width-large);
|
||||
}
|
||||
|
||||
.vc-voice-filters-md {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
padding-inline: 1.2rem;
|
||||
}
|
||||
|
||||
.vc-voice-filters-md-image {
|
||||
width: 100%;
|
||||
max-height: 20rem;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
margin-block: 0.5rem;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
background: var(--background-tertiary);
|
||||
border-radius: 0.5rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.vc-voice-filters-card {
|
||||
background: var(--background-secondary);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.vc-voice-filters-modal-link {
|
||||
border-radius: 3px;
|
||||
padding: 0 2px;
|
||||
font-weight: 500;
|
||||
unicode-bidi: plaintext;
|
||||
color: var(--mention-foreground);
|
||||
background: var(--mention-background);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
Loading…
Add table
Reference in a new issue