styleguide

This commit is contained in:
thororen1234 2026-02-14 16:00:46 -05:00
parent aed7f7207b
commit 82b8aae3ec
No known key found for this signature in database
2 changed files with 135 additions and 1 deletions

View file

@ -3,7 +3,7 @@ memory_config:
disabled: false
code_review:
disable: false
comment_severity_threshold: MEDIUM
comment_severity_threshold: LOW
max_review_comments: -1
pull_request_opened:
help: false

134
.gemini/styleguide.md Normal file
View file

@ -0,0 +1,134 @@
title: Equicord Review Guidelines
rules:
critical:
licenseHeader: NEVER edit existing license headers, new files use 2026
checkDeprecated: always verify components/functions arent deprecated before using
noAnyTypes: NEVER use any for Discord objects, import proper types from @vencord/discord-types
minimalChanges: fix with smallest possible change, no refactoring unless explicitly asked
noOverengineering: solve the problem at hand only, no patterns for patterns sake
humanText: write natural human text in errors, descriptions, messages. No dashes or robotic formatting. Write like "Module not found" not "Module - not found". Keep it simple and conversational
code:
deleteNotComment: DELETE dead code, dont comment it, git preserves history
noHardcoded: no hardcoded values, unused imports, magic numbers
useClassNameFactory: import classNameFactory from @utils/css (NOT @api/Styles which is deprecated), ALWAYS use cl() for class names, never hardcode strings like "vc-plugin-name-class"
useClasses: when combining multiple class names use classes() from @utils/misc, not template strings like `${a} ${b}`
useExisting: use utilities from @utils/, @api/, @components/
logger: use Logger from @utils/Logger, not console.log
descriptions: capital first letter, end with period
minimal: less code with same functionality is ALWAYS better
noComments: NEVER add comments unless explicitly asked
preserveComments: keep existing comments, they contain important context
performance: use Map/Set, .some/.find, no spread in loops, Promise.all
simple: KISS over clever, flat over nested, explicit over implicit
typescript:
prefer: optional chaining (?.), nullish coalescing (??), const, arrow functions
use: destructuring, template literals, object shorthand, array methods
style: early returns, trust inference, inline single-use variables
react:
errorBoundary: wrap complex components with ErrorBoundary.wrap(Component, { noop: true })
nullNotUndefined: return null for conditional rendering not undefined
forbidden: React.cloneElement, React.isValidElement, React.memo(), React.lazy, React.Children
cleanup: always return cleanup functions in useEffect
forbidden:
DOM: direct DOM manipulation (use patches)
cssOnly: CSS-only plugins
emptyCatch: empty catch blocks
hardcodedVars: hardcoded minified vars in patches (e,t,n)
settingsArrays: NEVER use settings.use() with arrays, mutate then reassign
antiPatterns{bad ⟹ good}:
"value !== null && value !== undefined" ⟹ "value"
"array && array.length > 0" ⟹ "array.length"
"settings?.store?.value" ⟹ "settings.store.value"
"value || defaultValue" ⟹ "value ?? defaultValue"
patching:
core:
surgical: minimum code touched, stability over cleverness, one patch per concern
iterate: try multiple approaches, keep simplifying until you find the cleanest solution
minimize: remove any regex character that doesnt change the match result
balanced: not overengineered, not overlooked, just clean and future-proof
find:
preferIntl: use #{intl::KEY} in find when possible, most stable anchor
makeUnique: if find matches multiple modules, add nearby stable string like "),icon:" or function name
combine: "#{intl::PIN_MESSAGE}),icon:" is better than generic strings
match:
shortestPath: match only what you need to replace, let find do the targeting
intlInMatch: can use #{intl::KEY} in match too, gets canonicalized to hash
simpleWins: /#{intl::KEY}\)/ beats /label:\i\.pinned\?.{0,60}#{intl::KEY}\)/
useAmpersand: $& keeps original, append/prepend to it
boundedGaps: .{0,50} not .+? or .*?
captureOnlyIfNeeded: only use capture groups if reusing in replace
NEVER:
hardcodedMinified: e,t,n,r,i,o,s,l,c,u,$_,xx,eD,eH,eW ⟹ use \i instead
minifiedChains: \i\.\i alone ⟹ surround with stable strings
unboundedGaps: .+? or .*? ⟹ use .{0,N}
genericPatterns: /className:\i/ alone ⟹ add anchor
rawIntlHash: .aA4Vce ⟹ #{intl::KEY_NAME}
examples:
clean: |
find: "#{intl::PIN_MESSAGE}),icon:"
match: /#{intl::PIN_MESSAGE}\)/
replace: "$self.getPinLabel(arguments[0]))"
append: |
find: "#{intl::VIEW_AS_ROLES_MENTIONS_WARNING}"
match: /#{intl::VIEW_AS_ROLES_MENTIONS_WARNING}.{0,100}(?=])/
replace: "$&,$self.renderTooltip(arguments[0].guild)"
wrap: |
find: "#{intl::THREE_USERS_TYPING}"
match: /(?<="aria-atomic":!0,children:)\i/
replace: "$self.renderTypingUsers({ children: $& })"
intl:
syntax: "#{intl::KEY}" or "#{intl::HASH::raw}" if key unknown
workflow: hash in code → reverse to get key → use #{intl::KEY} or fallback to #{intl::HASH::raw}
pluginInterop:
checkEnabled: isPluginEnabled(name) from @api/PluginManager takes a string, checks required/isDependency/enabled
import: import the plugin directly from its path to access .name and functions
avoid: dont use Vencord.Plugins.plugins or plugin.started, dont use "as unknown as" casting
example: |
import { isPluginEnabled } from "@api/PluginManager";
import otherPlugin from "@equicordplugins/otherPlugin";
if (!isPluginEnabled(otherPlugin.name)) return null;
otherPlugin.someFunction();
reference:
types: Channel, Guild, GuildMember, User, Role, Message from @vencord/discord-types
components: Paragraph, BaseText, Flex, Button, ErrorCard from @components/
settings: use definePluginSettings from @api/Settings, not inline settings object
utilities:
clipboard: copyToClipboard from @utils/clipboard
discord: insertTextIntoChatInputBox, getCurrentChannel, getCurrentGuild, getIntlMessage, openUserProfile, openPrivateChannel, sendMessage, copyWithToast, getUniqueUsername, fetchUserProfile from @utils/discord
css: classNameFactory, classNameToSelector from @utils/css
misc: classes, sleep, isObject, isObjectEmpty, pluralise, parseUrl, identity from @utils/misc
text: formatDuration, formatDurationMs, humanFriendlyJoin, makeCodeblock, toInlineCode, escapeRegExp from @utils/text
modal: openModal, closeModal, ModalRoot, ModalHeader, ModalContent, ModalCloseButton from @utils/modal
margins: Margins from @utils/margins (Margins.top8, Margins.bottom16, etc.)
guards: isTruthy, isNonNullish from @utils/guards
web: saveFile, chooseFile from @utils/web
webpackCommon:
icons: IconUtils from @webpack/common, NEVER hardcode cdn.discordapp.com URLs
getUserAvatarURL: IconUtils.getUserAvatarURL(user, canAnimate?, size?)
getDefaultAvatarURL: IconUtils.getDefaultAvatarURL(id)
getUserBannerURL: IconUtils.getUserBannerURL({ id, banner, canAnimate?, size })
getGuildIconURL: IconUtils.getGuildIconURL({ id, icon, size?, canAnimate? })
getGuildBannerURL: IconUtils.getGuildBannerURL(guild, canAnimate?)
getChannelIconURL: IconUtils.getChannelIconURL({ id, icon })
getEmojiURL: IconUtils.getEmojiURL({ id, animated, size })
getApplicationIconURL: IconUtils.getApplicationIconURL(data)
getGameAssetURL: IconUtils.getGameAssetURL(data)
stores: UserStore, GuildStore, ChannelStore, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, PresenceStore, RelationshipStore, MessageStore, EmojiStore, ThemeStore, PermissionStore, VoiceStateStore from @webpack/common
actions: RestAPI, FluxDispatcher, MessageActions, NavigationRouter, ChannelRouter, ChannelActionCreators, SettingsRouter from @webpack/common
utils: Constants (Constants.Endpoints), SnowflakeUtils, Parser, PermissionsBits, moment, lodash, ColorUtils, ImageUtils, DateUtils, UsernameUtils, DisplayProfileUtils from @webpack/common
components: Tooltip, TextInput, TextArea, Select, Slider, Avatar, Menu, Popout, ScrollerThin, Timestamp from @webpack/common
toasts: Toasts, showToast from @webpack/common
imports:
lazy: findByPropsLazy, findStoreLazy, findComponentByCodeLazy, findExportedComponentLazy from @webpack
common: useState, useEffect, useCallback, useStateFromStores from @webpack/common
neverHardcode:
cdnURLs: NEVER use "cdn.discordapp.com/avatars/", "cdn.discordapp.com/banners/", "cdn.discordapp.com/emojis/", "cdn.discordapp.com/icons/", "cdn.discordapp.com/channel-icons/" ⟹ use IconUtils
apiEndpoints: NEVER hardcode "/api/v9", "/users/@me" ⟹ use Constants.Endpoints or RestAPI
console: NEVER use console.log/warn/error ⟹ use Logger from @utils/Logger
classConcat: NEVER use `${a} ${b}` for class names ⟹ use classes(a, b) from @utils/misc
defaultAvatars: NEVER hardcode "/assets/*.png" for default avatars ⟹ use IconUtils.getDefaultAvatarURL(id)