Merge branch 'main' into cal/dev-124-project-validation
This commit is contained in:
commit
252b7bf965
@ -118,6 +118,7 @@ import {
|
||||
type Cape,
|
||||
type SkinModel,
|
||||
get_normalized_skin_texture,
|
||||
determineModelType,
|
||||
} from '@/helpers/skins.ts'
|
||||
import { handleError } from '@/store/notifications'
|
||||
import {
|
||||
@ -253,7 +254,7 @@ async function showNew(e: MouseEvent, skinTextureUrl: string) {
|
||||
mode.value = 'new'
|
||||
currentSkin.value = null
|
||||
uploadedTextureUrl.value = skinTextureUrl
|
||||
variant.value = 'CLASSIC'
|
||||
variant.value = await determineModelType(skinTextureUrl)
|
||||
selectedCape.value = undefined
|
||||
visibleCapeList.value = []
|
||||
initVisibleCapeList()
|
||||
|
||||
@ -128,6 +128,14 @@ const messages = defineMessages({
|
||||
id: 'instance.worlds.game_already_open',
|
||||
defaultMessage: 'Instance is already open',
|
||||
},
|
||||
noContact: {
|
||||
id: 'instance.worlds.no_contact',
|
||||
defaultMessage: "Server couldn't be contacted",
|
||||
},
|
||||
incompatibleServer: {
|
||||
id: 'instance.worlds.incompatible_server',
|
||||
defaultMessage: 'Server is incompatible',
|
||||
},
|
||||
copyAddress: {
|
||||
id: 'instance.worlds.copy_address',
|
||||
defaultMessage: 'Copy address',
|
||||
@ -302,7 +310,6 @@ const messages = defineMessages({
|
||||
</template>
|
||||
</div>
|
||||
<div class="flex gap-1 justify-end smart-clickable:allow-pointer-events">
|
||||
<template v-if="world.type === 'singleplayer' || serverStatus">
|
||||
<ButtonStyled
|
||||
v-if="(playingWorld || (locked && playingInstance)) && !startingInstance"
|
||||
color="red"
|
||||
@ -315,8 +322,10 @@ const messages = defineMessages({
|
||||
<ButtonStyled v-else>
|
||||
<button
|
||||
v-tooltip="
|
||||
serverIncompatible
|
||||
? 'Server is incompatible'
|
||||
!serverStatus
|
||||
? formatMessage(messages.noContact)
|
||||
: serverIncompatible
|
||||
? formatMessage(messages.incompatibleServer)
|
||||
: !supportsQuickPlay
|
||||
? formatMessage(messages.noQuickPlay)
|
||||
: playingOtherWorld || locked
|
||||
@ -331,13 +340,6 @@ const messages = defineMessages({
|
||||
{{ formatMessage(commonMessages.playButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
<ButtonStyled v-else>
|
||||
<button class="invisible">
|
||||
<PlayIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.playButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu
|
||||
:options="[
|
||||
|
||||
@ -62,16 +62,14 @@ export async function determineModelType(texture: string): Promise<'SLIM' | 'CLA
|
||||
|
||||
context.drawImage(image, 0, 0)
|
||||
|
||||
const armX = 44
|
||||
const armY = 16
|
||||
const armWidth = 4
|
||||
const armX = 54
|
||||
const armY = 20
|
||||
const armWidth = 2
|
||||
const armHeight = 12
|
||||
|
||||
const imageData = context.getImageData(armX, armY, armWidth, armHeight).data
|
||||
|
||||
for (let y = 0; y < armHeight; y++) {
|
||||
const alphaIndex = (3 + y * armWidth) * 4 + 3
|
||||
if (imageData[alphaIndex] !== 0) {
|
||||
for (let index = 1; index <= imageData.length; index++) {
|
||||
//every fourth value in RGBA is the alpha channel
|
||||
if (index % 4 == 0 && imageData[index - 1] !== 0) {
|
||||
resolve('CLASSIC')
|
||||
return
|
||||
}
|
||||
|
||||
@ -377,6 +377,12 @@
|
||||
"instance.worlds.hardcore": {
|
||||
"message": "Hardcore mode"
|
||||
},
|
||||
"instance.worlds.incompatible_server": {
|
||||
"message": "Server is incompatible"
|
||||
},
|
||||
"instance.worlds.no_contact": {
|
||||
"message": "Server couldn't be contacted"
|
||||
},
|
||||
"instance.worlds.no_quick_play": {
|
||||
"message": "You can only jump straight into worlds on Minecraft 1.20+"
|
||||
},
|
||||
|
||||
@ -38,10 +38,10 @@
|
||||
"@intercom/messenger-js-sdk": "^0.0.14",
|
||||
"@ltd/j-toml": "^1.38.0",
|
||||
"@modrinth/assets": "workspace:*",
|
||||
"@modrinth/ui": "workspace:*",
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"@modrinth/blog": "workspace:*",
|
||||
"@modrinth/moderation": "workspace:*",
|
||||
"@modrinth/ui": "workspace:*",
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"@pinia/nuxt": "^0.5.1",
|
||||
"@types/three": "^0.172.0",
|
||||
"@vintl/vintl": "^4.4.1",
|
||||
@ -59,6 +59,7 @@
|
||||
"markdown-it": "14.1.0",
|
||||
"pathe": "^1.1.2",
|
||||
"pinia": "^2.1.7",
|
||||
"prettier": "^3.6.2",
|
||||
"qrcode.vue": "^3.4.0",
|
||||
"semver": "^7.5.4",
|
||||
"three": "^0.172.0",
|
||||
|
||||
@ -115,10 +115,12 @@ html {
|
||||
--shadow-inset-sm: inset 0px -1px 2px hsla(221, 39%, 11%, 0.15);
|
||||
|
||||
--shadow-raised-lg: 0px 2px 4px hsla(221, 39%, 11%, 0.2);
|
||||
--shadow-raised: 0.3px 0.5px 0.6px hsl(var(--shadow-color) / 0.15),
|
||||
--shadow-raised:
|
||||
0.3px 0.5px 0.6px hsl(var(--shadow-color) / 0.15),
|
||||
1px 2px 2.2px -1.7px hsl(var(--shadow-color) / 0.12),
|
||||
4.4px 8.8px 9.7px -3.4px hsl(var(--shadow-color) / 0.09);
|
||||
--shadow-floating: hsla(0, 0%, 0%, 0) 0px 0px 0px 0px, hsla(0, 0%, 0%, 0) 0px 0px 0px 0px,
|
||||
--shadow-floating:
|
||||
hsla(0, 0%, 0%, 0) 0px 0px 0px 0px, hsla(0, 0%, 0%, 0) 0px 0px 0px 0px,
|
||||
hsla(0, 0%, 0%, 0.1) 0px 4px 6px -1px, hsla(0, 0%, 0%, 0.1) 0px 2px 4px -1px;
|
||||
|
||||
--shadow-card: rgba(50, 50, 100, 0.1) 0px 2px 4px 0px;
|
||||
@ -150,8 +152,8 @@ html {
|
||||
rgba(255, 255, 255, 0.35) 0%,
|
||||
rgba(255, 255, 255, 0.2695) 100%
|
||||
);
|
||||
--landing-blob-shadow: 2px 2px 12px rgba(0, 0, 0, 0.16),
|
||||
inset 2px 2px 64px rgba(255, 255, 255, 0.45);
|
||||
--landing-blob-shadow:
|
||||
2px 2px 12px rgba(0, 0, 0, 0.16), inset 2px 2px 64px rgba(255, 255, 255, 0.45);
|
||||
|
||||
--landing-card-bg: rgba(255, 255, 255, 0.8);
|
||||
--landing-card-shadow: 2px 2px 12px rgba(0, 0, 0, 0.16);
|
||||
@ -251,13 +253,15 @@ html {
|
||||
|
||||
--shadow-raised-lg: 0px 2px 4px hsla(221, 39%, 11%, 0.2);
|
||||
--shadow-raised: 0px -2px 4px hsla(221, 39%, 11%, 0.1);
|
||||
--shadow-floating: hsla(0, 0%, 0%, 0) 0px 0px 0px 0px, hsla(0, 0%, 0%, 0) 0px 0px 0px 0px,
|
||||
--shadow-floating:
|
||||
hsla(0, 0%, 0%, 0) 0px 0px 0px 0px, hsla(0, 0%, 0%, 0) 0px 0px 0px 0px,
|
||||
hsla(0, 0%, 0%, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;
|
||||
|
||||
--shadow-card: rgba(0, 0, 0, 0.25) 0px 2px 4px 0px;
|
||||
|
||||
--landing-maze-bg: url("https://cdn.modrinth.com/landing-new/landing.webp");
|
||||
--landing-maze-gradient-bg: linear-gradient(0deg, #31375f 0%, rgba(8, 14, 55, 0) 100%),
|
||||
--landing-maze-gradient-bg:
|
||||
linear-gradient(0deg, #31375f 0%, rgba(8, 14, 55, 0) 100%),
|
||||
url("https://cdn.modrinth.com/landing-new/landing-lower.webp");
|
||||
--landing-maze-outer-bg: linear-gradient(180deg, #06060d 0%, #000000 100%);
|
||||
|
||||
@ -284,7 +288,8 @@ html {
|
||||
rgba(44, 48, 79, 0.35) 0%,
|
||||
rgba(32, 35, 50, 0.2695) 100%
|
||||
);
|
||||
--landing-blob-shadow: 2px 2px 12px rgba(0, 0, 0, 0.16), inset 2px 2px 64px rgba(57, 61, 94, 0.45);
|
||||
--landing-blob-shadow:
|
||||
2px 2px 12px rgba(0, 0, 0, 0.16), inset 2px 2px 64px rgba(57, 61, 94, 0.45);
|
||||
|
||||
--landing-card-bg: rgba(59, 63, 85, 0.15);
|
||||
--landing-card-shadow: 2px 2px 12px rgba(0, 0, 0, 0.16);
|
||||
@ -360,8 +365,9 @@ body {
|
||||
// Defaults
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
--font-standard: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Roboto,
|
||||
Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
--font-standard:
|
||||
Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Oxygen, Ubuntu, Roboto, Cantarell,
|
||||
Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
--mono-font: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
|
||||
font-family: var(--font-standard);
|
||||
font-size: 16px;
|
||||
|
||||
@ -365,7 +365,6 @@ function getJudgements(): ModerationJudgements {
|
||||
const judgements: ModerationJudgements = {};
|
||||
|
||||
modPackData.value.forEach((item) => {
|
||||
if (item.status && item.status !== "unidentified") {
|
||||
if (item.type === "flame") {
|
||||
judgements[item.sha1] = {
|
||||
type: "flame",
|
||||
@ -385,7 +384,6 @@ function getJudgements(): ModerationJudgements {
|
||||
file_name: item.file_name,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return judgements;
|
||||
|
||||
@ -116,8 +116,10 @@
|
||||
<span class="label__title">{{ action.label }}</span>
|
||||
</label>
|
||||
<DropdownSelect
|
||||
:max-visible-options="3"
|
||||
render-up
|
||||
:name="`dropdown-${getActionId(action)}`"
|
||||
:options="action.options"
|
||||
:options="getVisibleDropdownOptions(action)"
|
||||
:model-value="getDropdownValue(action)"
|
||||
:placeholder="'Select an option'"
|
||||
:disabled="false"
|
||||
@ -138,7 +140,7 @@
|
||||
<div class="mb-2 font-semibold">{{ action.label }}</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<ButtonStyled
|
||||
v-for="(option, optIndex) in action.options"
|
||||
v-for="(option, optIndex) in getVisibleMultiSelectOptions(action)"
|
||||
:key="`${getActionId(action)}-chip-${optIndex}`"
|
||||
:color="isChipSelected(action, optIndex) ? 'brand' : 'standard'"
|
||||
@click="toggleChip(action, optIndex)"
|
||||
@ -380,6 +382,7 @@ import type {
|
||||
import ModpackPermissionsFlow from "./ModpackPermissionsFlow.vue";
|
||||
import KeybindsModal from "./ChecklistKeybindsModal.vue";
|
||||
import { finalPermissionMessages } from "@modrinth/moderation/data/modpack-permissions-stage";
|
||||
import prettier from "prettier";
|
||||
|
||||
const keybindsModal = ref<InstanceType<typeof KeybindsModal>>();
|
||||
|
||||
@ -461,7 +464,7 @@ const stageTextExpanded = computedAsync(async () => {
|
||||
const stage = checklist[stageIndex];
|
||||
if (stage.text) {
|
||||
return renderHighlightedString(
|
||||
expandVariables(await stage.text(), props.project, variables.value),
|
||||
expandVariables(await stage.text(props.project), props.project, variables.value),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@ -559,15 +562,21 @@ function handleKeybinds(event: KeyboardEvent) {
|
||||
},
|
||||
trySelectDropdownOption: (actionIndex: number, optionIndex: number) => {
|
||||
const action = visibleActions.value[actionIndex] as DropdownAction;
|
||||
if (action && action.type === "dropdown" && action.options[optionIndex]) {
|
||||
selectDropdownOption(action, action.options[optionIndex]);
|
||||
if (action && action.type === "dropdown") {
|
||||
const visibleOptions = getVisibleDropdownOptions(action);
|
||||
if (optionIndex < visibleOptions.length) {
|
||||
selectDropdownOption(action, visibleOptions[optionIndex]);
|
||||
}
|
||||
}
|
||||
},
|
||||
tryToggleChip: (actionIndex: number, chipIndex: number) => {
|
||||
const action = visibleActions.value[actionIndex] as MultiSelectChipsAction;
|
||||
if (action && action.type === "multi-select-chips") {
|
||||
const visibleOptions = getVisibleMultiSelectOptions(action);
|
||||
if (chipIndex < visibleOptions.length) {
|
||||
toggleChip(action, chipIndex);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
tryFocusNextAction: () => {
|
||||
@ -733,13 +742,17 @@ const multiSelectActions = computed(() =>
|
||||
|
||||
function getDropdownValue(action: DropdownAction) {
|
||||
const actionId = getActionId(action);
|
||||
const visibleOptions = getVisibleDropdownOptions(action);
|
||||
const currentValue = actionStates.value[actionId]?.value ?? action.defaultOption ?? 0;
|
||||
|
||||
if (action.options && action.options[currentValue]) {
|
||||
return action.options[currentValue];
|
||||
const allOptions = action.options;
|
||||
const storedOption = allOptions[currentValue];
|
||||
|
||||
if (storedOption && visibleOptions.includes(storedOption)) {
|
||||
return storedOption;
|
||||
}
|
||||
|
||||
return action.options?.[0] || null;
|
||||
return visibleOptions[0] || null;
|
||||
}
|
||||
|
||||
function isActionSelected(action: Action): boolean {
|
||||
@ -775,22 +788,33 @@ function selectDropdownOption(action: DropdownAction, selected: any) {
|
||||
function isChipSelected(action: MultiSelectChipsAction, optionIndex: number): boolean {
|
||||
const actionId = getActionId(action);
|
||||
const selectedSet = actionStates.value[actionId]?.value as Set<number> | undefined;
|
||||
return selectedSet?.has(optionIndex) || false;
|
||||
|
||||
const visibleOptions = getVisibleMultiSelectOptions(action);
|
||||
const visibleOption = visibleOptions[optionIndex];
|
||||
const originalIndex = action.options.findIndex((opt) => opt === visibleOption);
|
||||
|
||||
return selectedSet?.has(originalIndex) || false;
|
||||
}
|
||||
|
||||
function toggleChip(action: MultiSelectChipsAction, optionIndex: number) {
|
||||
const actionId = getActionId(action);
|
||||
const state = actionStates.value[actionId];
|
||||
if (state && state.value instanceof Set) {
|
||||
if (state.value.has(optionIndex)) {
|
||||
state.value.delete(optionIndex);
|
||||
const visibleOptions = getVisibleMultiSelectOptions(action);
|
||||
const visibleOption = visibleOptions[optionIndex];
|
||||
const originalIndex = action.options.findIndex((opt) => opt === visibleOption);
|
||||
|
||||
if (originalIndex !== -1) {
|
||||
if (state.value.has(originalIndex)) {
|
||||
state.value.delete(originalIndex);
|
||||
} else {
|
||||
state.value.add(optionIndex);
|
||||
state.value.add(originalIndex);
|
||||
}
|
||||
state.selected = state.value.size > 0;
|
||||
persistState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isAnyVisibleInputs = computed(() => {
|
||||
return visibleActions.value.some((action) => {
|
||||
@ -869,9 +893,23 @@ async function processAction(
|
||||
stageIndex: number,
|
||||
messageParts: MessagePart[],
|
||||
) {
|
||||
const allValidActionIds: string[] = [];
|
||||
checklist.forEach((stage, stageIdx) => {
|
||||
stage.actions.forEach((stageAction, actionIdx) => {
|
||||
allValidActionIds.push(getActionIdForStage(stageAction, stageIdx, actionIdx));
|
||||
if (stageAction.enablesActions) {
|
||||
stageAction.enablesActions.forEach((enabledAction, enabledIdx) => {
|
||||
allValidActionIds.push(
|
||||
getActionIdForStage(enabledAction, stageIdx, actionIdx, enabledIdx),
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (action.type === "button" || action.type === "toggle") {
|
||||
const buttonAction = action as ButtonAction | ToggleAction;
|
||||
const message = await getActionMessage(buttonAction, selectedActionIds);
|
||||
const message = await getActionMessage(buttonAction, selectedActionIds, allValidActionIds);
|
||||
if (message) {
|
||||
messageParts.push({
|
||||
weight: buttonAction.weight,
|
||||
@ -885,16 +923,29 @@ async function processAction(
|
||||
const matchingVariant = findMatchingVariant(
|
||||
conditionalAction.messageVariants,
|
||||
selectedActionIds,
|
||||
allValidActionIds,
|
||||
stageIndex,
|
||||
);
|
||||
|
||||
let message: string;
|
||||
let weight: number;
|
||||
|
||||
if (matchingVariant) {
|
||||
const message = (await matchingVariant.message()) as string;
|
||||
message = (await matchingVariant.message()) as string;
|
||||
weight = matchingVariant.weight;
|
||||
} else if (conditionalAction.fallbackMessage) {
|
||||
message = (await conditionalAction.fallbackMessage()) as string;
|
||||
weight = conditionalAction.fallbackWeight ?? 0;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
messageParts.push({
|
||||
weight: matchingVariant.weight,
|
||||
weight,
|
||||
content: processMessage(message, action, stageIndex, textInputValues.value),
|
||||
actionId,
|
||||
stageIndex,
|
||||
});
|
||||
}
|
||||
} else if (action.type === "dropdown") {
|
||||
const dropdownAction = action as DropdownAction;
|
||||
const selectedIndex = state.value ?? 0;
|
||||
@ -929,6 +980,18 @@ async function processAction(
|
||||
}
|
||||
|
||||
function shouldShowStage(stage: Stage): boolean {
|
||||
let hasVisibleActions = false;
|
||||
|
||||
for (const a of stage.actions) {
|
||||
if (shouldShowAction(a)) {
|
||||
hasVisibleActions = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasVisibleActions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof stage.shouldShow === "function") {
|
||||
return stage.shouldShow(props.project);
|
||||
}
|
||||
@ -944,6 +1007,24 @@ function shouldShowAction(action: Action): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
function getVisibleDropdownOptions(action: DropdownAction) {
|
||||
return action.options.filter((option) => {
|
||||
if (typeof option.shouldShow === "function") {
|
||||
return option.shouldShow(props.project);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function getVisibleMultiSelectOptions(action: MultiSelectChipsAction) {
|
||||
return action.options.filter((option) => {
|
||||
if (typeof option.shouldShow === "function") {
|
||||
return option.shouldShow(props.project);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function shouldShowStageIndex(stageIndex: number): boolean {
|
||||
return shouldShowStage(checklist[stageIndex]);
|
||||
}
|
||||
@ -1021,7 +1102,20 @@ async function generateMessage() {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const formattedMessage = await prettier.format(fullMessage, {
|
||||
parser: "markdown",
|
||||
printWidth: 80,
|
||||
proseWrap: "always",
|
||||
tabWidth: 2,
|
||||
useTabs: false,
|
||||
});
|
||||
message.value = formattedMessage;
|
||||
} catch (formattingError) {
|
||||
console.warn("Failed to format markdown, using original:", formattingError);
|
||||
message.value = fullMessage;
|
||||
}
|
||||
|
||||
generatedMessage.value = true;
|
||||
} catch (error) {
|
||||
console.error("Error generating message:", error);
|
||||
|
||||
@ -172,6 +172,7 @@ const flags = useFeatureFlags();
|
||||
|
||||
.markdown-body {
|
||||
grid-area: body;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.reporter-info {
|
||||
|
||||
@ -194,13 +194,12 @@ export class ModrinthServer {
|
||||
}
|
||||
|
||||
async testNodeReachability(): Promise<boolean> {
|
||||
if (!this.general?.datacenter) {
|
||||
console.warn("No datacenter info available for ping test");
|
||||
if (!this.general?.node?.instance) {
|
||||
console.warn("No node instance available for ping test");
|
||||
return false;
|
||||
}
|
||||
|
||||
const datacenter = this.general.datacenter;
|
||||
const wsUrl = `wss://${datacenter}.nodes.modrinth.com/pingtest`;
|
||||
const wsUrl = `wss://${this.general.node.instance}/pingtest`;
|
||||
|
||||
try {
|
||||
return await new Promise((resolve) => {
|
||||
|
||||
@ -112,7 +112,8 @@ export async function useServersFetch<T>(
|
||||
const response = await $fetch<T>(fullUrl, {
|
||||
method,
|
||||
headers,
|
||||
body: body && contentType === "application/json" ? JSON.stringify(body) : body ?? undefined,
|
||||
body:
|
||||
body && contentType === "application/json" ? JSON.stringify(body) : (body ?? undefined),
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
|
||||
@ -1346,6 +1346,15 @@ const footerLinks = [
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
href: "/legal/copyright",
|
||||
label: formatMessage(
|
||||
defineMessage({
|
||||
id: "layout.footer.legal.copyright-policy",
|
||||
defaultMessage: "Copyright Policy and DMCA",
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@ -404,6 +404,9 @@
|
||||
"layout.footer.legal-disclaimer": {
|
||||
"message": "NOT AN OFFICIAL MINECRAFT SERVICE. NOT APPROVED BY OR ASSOCIATED WITH MOJANG OR MICROSOFT."
|
||||
},
|
||||
"layout.footer.legal.copyright-policy": {
|
||||
"message": "Copyright Policy and DMCA"
|
||||
},
|
||||
"layout.footer.legal.privacy-policy": {
|
||||
"message": "Privacy Policy"
|
||||
},
|
||||
|
||||
@ -29,12 +29,11 @@
|
||||
class="settings-header__icon"
|
||||
/>
|
||||
<div class="settings-header__text">
|
||||
<h1 class="wrap-as-needed">
|
||||
{{ project.title }}
|
||||
</h1>
|
||||
<h1 class="wrap-as-needed">{{ project.title }}</h1>
|
||||
<ProjectStatusBadge :status="project.status" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Project settings</h2>
|
||||
<NavStack>
|
||||
<NavStackItem
|
||||
@ -111,6 +110,7 @@
|
||||
</NavStack>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div class="normal-page__content">
|
||||
<ProjectMemberHeader
|
||||
v-if="currentMember"
|
||||
@ -145,6 +145,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="experimental-styles-within">
|
||||
<NewModal ref="settingsModal">
|
||||
<template #title>
|
||||
@ -174,9 +175,11 @@
|
||||
<div
|
||||
class="animation-ring-3 flex items-center justify-center rounded-full border-4 border-solid border-brand bg-brand-highlight opacity-40"
|
||||
></div>
|
||||
|
||||
<div
|
||||
class="animation-ring-2 flex items-center justify-center rounded-full border-4 border-solid border-brand bg-brand-highlight opacity-60"
|
||||
></div>
|
||||
|
||||
<div
|
||||
class="animation-ring-1 flex items-center justify-center rounded-full border-4 border-solid border-brand bg-brand-highlight"
|
||||
>
|
||||
@ -219,8 +222,7 @@
|
||||
:href="`modrinth://mod/${project.slug}`"
|
||||
@click="() => installWithApp()"
|
||||
>
|
||||
<ModrinthIcon aria-hidden="true" />
|
||||
Install with Modrinth App
|
||||
<ModrinthIcon aria-hidden="true" /> Install with Modrinth App
|
||||
<ExternalIcon aria-hidden="true" />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
@ -240,6 +242,7 @@
|
||||
<div class="flex h-[2px] w-full rounded-2xl bg-button-bg"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mx-auto flex w-fit flex-col gap-2">
|
||||
<ButtonStyled v-if="project.game_versions.length === 1">
|
||||
<div class="disabled button-like">
|
||||
@ -327,8 +330,7 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
{{ gameVersion }}
|
||||
<CheckIcon v-if="userSelectedGameVersion === gameVersion" />
|
||||
{{ gameVersion }} <CheckIcon v-if="userSelectedGameVersion === gameVersion" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</ScrollablePanel>
|
||||
@ -419,7 +421,6 @@
|
||||
</ScrollablePanel>
|
||||
</Accordion>
|
||||
</div>
|
||||
|
||||
<AutomaticAccordion div class="flex flex-col gap-2">
|
||||
<VersionSummary
|
||||
v-if="filteredRelease"
|
||||
@ -489,11 +490,11 @@
|
||||
:color="route.name === 'type-id-version-version' ? `standard` : `brand`"
|
||||
>
|
||||
<button @click="(event) => downloadModal.show(event)">
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
Download
|
||||
<DownloadIcon aria-hidden="true" /> Download
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
|
||||
<div class="contents sm:hidden">
|
||||
<ButtonStyled
|
||||
size="large"
|
||||
@ -558,9 +559,11 @@
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
|
||||
<p class="m-0 text-wrap text-sm font-medium leading-tight text-secondary">
|
||||
Modrinth Servers is the easiest way to play with your friends without hassle!
|
||||
</p>
|
||||
|
||||
<p class="m-0 text-wrap text-sm font-bold text-primary">
|
||||
Starting at $5<span class="text-xs"> / month</span>
|
||||
</p>
|
||||
@ -625,6 +628,7 @@
|
||||
{{ option.name }}
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
||||
<div v-else class="menu-text">
|
||||
<p class="popout-text">No collections found.</p>
|
||||
</div>
|
||||
@ -632,8 +636,7 @@
|
||||
class="btn collection-button"
|
||||
@click="(event) => $refs.modal_collection.show(event)"
|
||||
>
|
||||
<PlusIcon aria-hidden="true" />
|
||||
Create new collection
|
||||
<PlusIcon aria-hidden="true" /> Create new collection
|
||||
</button>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
@ -716,25 +719,14 @@
|
||||
:dropdown-id="`${baseId}-more-options`"
|
||||
>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #analytics>
|
||||
<ChartIcon aria-hidden="true" />
|
||||
Analytics
|
||||
</template>
|
||||
<template #analytics> <ChartIcon aria-hidden="true" /> Analytics </template>
|
||||
<template #moderation-checklist>
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
Review project
|
||||
</template>
|
||||
<template #report>
|
||||
<ReportIcon aria-hidden="true" />
|
||||
Report
|
||||
</template>
|
||||
<template #copy-id>
|
||||
<ClipboardCopyIcon aria-hidden="true" />
|
||||
Copy ID
|
||||
<ScaleIcon aria-hidden="true" /> Review project
|
||||
</template>
|
||||
<template #report> <ReportIcon aria-hidden="true" /> Report </template>
|
||||
<template #copy-id> <ClipboardCopyIcon aria-hidden="true" /> Copy ID </template>
|
||||
<template #copy-permalink>
|
||||
<ClipboardCopyIcon aria-hidden="true" />
|
||||
Copy permanent link
|
||||
<ClipboardCopyIcon aria-hidden="true" /> Copy permanent link
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
@ -760,6 +752,7 @@
|
||||
updates unless the author decides to unarchive the project.
|
||||
</MessageBanner>
|
||||
</div>
|
||||
|
||||
<div class="normal-page__sidebar">
|
||||
<ProjectSidebarCompatibility
|
||||
:project="project"
|
||||
@ -789,6 +782,7 @@
|
||||
/>
|
||||
<div class="card flex-card experimental-styles-within">
|
||||
<h2>{{ formatMessage(detailsMessages.title) }}</h2>
|
||||
|
||||
<div class="details-list">
|
||||
<div class="details-list__item">
|
||||
<BookTextIcon aria-hidden="true" />
|
||||
@ -817,53 +811,48 @@
|
||||
<span v-else>{{ licenseIdDisplay }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="project.approved"
|
||||
v-tooltip="$dayjs(project.approved).format('MMMM D, YYYY [at] h:mm A')"
|
||||
class="details-list__item"
|
||||
>
|
||||
<CalendarIcon aria-hidden="true" />
|
||||
<div>
|
||||
{{ formatMessage(detailsMessages.published, { date: publishedDate }) }}
|
||||
</div>
|
||||
<div>{{ formatMessage(detailsMessages.published, { date: publishedDate }) }}</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
v-tooltip="$dayjs(project.published).format('MMMM D, YYYY [at] h:mm A')"
|
||||
class="details-list__item"
|
||||
>
|
||||
<CalendarIcon aria-hidden="true" />
|
||||
<div>
|
||||
{{ formatMessage(detailsMessages.created, { date: createdDate }) }}
|
||||
</div>
|
||||
<div>{{ formatMessage(detailsMessages.created, { date: createdDate }) }}</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="project.status === 'processing' && project.queued"
|
||||
v-tooltip="$dayjs(project.queued).format('MMMM D, YYYY [at] h:mm A')"
|
||||
class="details-list__item"
|
||||
>
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
<div>
|
||||
{{ formatMessage(detailsMessages.submitted, { date: submittedDate }) }}
|
||||
</div>
|
||||
<div>{{ formatMessage(detailsMessages.submitted, { date: submittedDate }) }}</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="versions.length > 0 && project.updated"
|
||||
v-tooltip="$dayjs(project.updated).format('MMMM D, YYYY [at] h:mm A')"
|
||||
class="details-list__item"
|
||||
>
|
||||
<VersionIcon aria-hidden="true" />
|
||||
<div>
|
||||
{{ formatMessage(detailsMessages.updated, { date: updatedDate }) }}
|
||||
</div>
|
||||
<div>{{ formatMessage(detailsMessages.updated, { date: updatedDate }) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="normal-page__content">
|
||||
<div class="overflow-x-auto">
|
||||
<NavTabs :links="navLinks" class="mb-4" />
|
||||
</div>
|
||||
<div class="overflow-x-auto"><NavTabs :links="navLinks" class="mb-4" /></div>
|
||||
<NuxtPage
|
||||
v-model:project="project"
|
||||
v-model:versions="versions"
|
||||
@ -881,6 +870,7 @@
|
||||
@delete-version="deleteVersion"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="normal-page__ultimate-sidebar">
|
||||
<!-- Uncomment this to enable the old moderation checklist. -->
|
||||
<!-- <ModerationChecklist
|
||||
@ -895,6 +885,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="auth.user && tags.staffRoles.includes(auth.user.role) && showModerationChecklist"
|
||||
class="moderation-checklist"
|
||||
@ -908,6 +899,7 @@
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
BookmarkIcon,
|
||||
@ -1570,8 +1562,6 @@ async function copyPermalink() {
|
||||
|
||||
const collapsedChecklist = ref(false);
|
||||
|
||||
console.log(project.value.id);
|
||||
|
||||
const showModerationChecklist = useLocalStorage(
|
||||
`show-moderation-checklist-${project.value.id}`,
|
||||
false,
|
||||
@ -1663,6 +1653,7 @@ const navLinks = computed(() => {
|
||||
];
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settings-header {
|
||||
display: flex;
|
||||
|
||||
@ -705,9 +705,9 @@ export default defineNuxtComponent({
|
||||
}
|
||||
|
||||
.gallery-body {
|
||||
flex-grow: 1;
|
||||
width: calc(100% - 2 * var(--spacing-card-md));
|
||||
padding: var(--spacing-card-sm) var(--spacing-card-md);
|
||||
overflow-wrap: anywhere;
|
||||
|
||||
.gallery-info {
|
||||
h2 {
|
||||
|
||||
@ -1421,7 +1421,8 @@ useSeoMeta({
|
||||
width: 25rem;
|
||||
height: 25rem;
|
||||
opacity: 0.75;
|
||||
background: radial-gradient(
|
||||
background:
|
||||
radial-gradient(
|
||||
50% 50% at 50% 50%,
|
||||
rgba(5, 206, 69, 0.19) 0%,
|
||||
rgba(15, 19, 49, 0.25) 100%
|
||||
|
||||
@ -266,12 +266,12 @@ const getRangeOfMethod = (method) => {
|
||||
|
||||
const maxWithdrawAmount = computed(() => {
|
||||
const interval = selectedMethod.value.interval;
|
||||
return interval?.standard ? interval.standard.max : interval?.fixed?.values.slice(-1)[0] ?? 0;
|
||||
return interval?.standard ? interval.standard.max : (interval?.fixed?.values.slice(-1)[0] ?? 0);
|
||||
});
|
||||
|
||||
const minWithdrawAmount = computed(() => {
|
||||
const interval = selectedMethod.value.interval;
|
||||
return interval?.standard ? interval.standard.min : interval?.fixed?.values?.[0] ?? fees.value;
|
||||
return interval?.standard ? interval.standard.min : (interval?.fixed?.values?.[0] ?? fees.value);
|
||||
});
|
||||
|
||||
const withdrawAccount = computed(() => {
|
||||
|
||||
@ -149,7 +149,8 @@ onMounted(() => {
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.main-hero {
|
||||
background: linear-gradient(360deg, rgba(199, 138, 255, 0.2) 10.92%, var(--color-bg) 100%),
|
||||
background:
|
||||
linear-gradient(360deg, rgba(199, 138, 255, 0.2) 10.92%, var(--color-bg) 100%),
|
||||
var(--color-accent-contrast);
|
||||
margin-top: -5rem;
|
||||
padding: 11.25rem 1rem 8rem;
|
||||
|
||||
@ -427,11 +427,8 @@
|
||||
Do Modrinth Servers have DDoS protection?
|
||||
</summary>
|
||||
<p class="m-0 ml-6 leading-[160%]">
|
||||
Yes. All Modrinth Servers come with DDoS protection powered by
|
||||
<a href="https://us.ovhcloud.com/security/anti-ddos/" target="_blank"
|
||||
>OVHcloud® Anti-DDoS infrastructure</a
|
||||
>
|
||||
which has over 17Tbps capacity. Your server is safe on Modrinth.
|
||||
Yes. All Modrinth Servers come with DDoS protection, with up to 17Tbps capacity in
|
||||
some locations.
|
||||
</p>
|
||||
</details>
|
||||
|
||||
@ -443,8 +440,9 @@
|
||||
Where are Modrinth Servers located? Can I choose a region?
|
||||
</summary>
|
||||
<p class="m-0 ml-6 leading-[160%]">
|
||||
We have servers in both North America in Vint Hill, Virginia, and Europe in Limburg,
|
||||
Germany. More regions to come in the future!
|
||||
We have servers available in North America and Europe at the moment that you can
|
||||
choose upon purchase. More regions to come in the future! If you'd like to switch
|
||||
your region, please contact support.
|
||||
</p>
|
||||
</details>
|
||||
|
||||
|
||||
@ -1020,7 +1020,7 @@ const nodeUnavailableDetails = computed(() => [
|
||||
{
|
||||
label: "Error message",
|
||||
value: nodeAccessible.value
|
||||
? server.moduleErrors?.general?.error.message ?? "Unknown"
|
||||
? (server.moduleErrors?.general?.error.message ?? "Unknown")
|
||||
: "Unable to reach node. Ping test failed.",
|
||||
type: "block" as const,
|
||||
},
|
||||
@ -1277,7 +1277,8 @@ useHead({
|
||||
background-repeat: no-repeat;
|
||||
filter: blur(1rem);
|
||||
content: "";
|
||||
background-image: linear-gradient(
|
||||
background-image:
|
||||
linear-gradient(
|
||||
to bottom,
|
||||
rgba(from var(--color-raised-bg) r g b / 0.2),
|
||||
rgb(from var(--color-raised-bg) r g b / 0.8)
|
||||
|
||||
@ -101,7 +101,7 @@
|
||||
<span :class="{ invisible: 'current_file' in op && !op.current_file }">
|
||||
{{
|
||||
"current_file" in op
|
||||
? op.current_file?.split("/")?.pop() ?? "unknown"
|
||||
? (op.current_file?.split("/")?.pop() ?? "unknown")
|
||||
: "unknown"
|
||||
}}
|
||||
</span>
|
||||
|
||||
@ -98,13 +98,6 @@
|
||||
"date": "2023-02-01T20:00:00.000Z",
|
||||
"link": "https://modrinth.com/news/article/accelerating-development"
|
||||
},
|
||||
{
|
||||
"title": "Two years of Modrinth: a retrospective",
|
||||
"summary": "The history of Modrinth as we know it from December 2020 to December 2022.",
|
||||
"thumbnail": "https://modrinth.com/news/default.webp",
|
||||
"date": "2023-01-07T00:00:00.000Z",
|
||||
"link": "https://modrinth.com/news/article/two-years-of-modrinth-history"
|
||||
},
|
||||
{
|
||||
"title": "Modrinth's Anniversary Update",
|
||||
"summary": "Marking two years of Modrinth and discussing our New Year's Resolutions for 2023.",
|
||||
@ -112,6 +105,13 @@
|
||||
"date": "2023-01-07T00:00:00.000Z",
|
||||
"link": "https://modrinth.com/news/article/two-years-of-modrinth"
|
||||
},
|
||||
{
|
||||
"title": "Two years of Modrinth: a retrospective",
|
||||
"summary": "The history of Modrinth as we know it from December 2020 to December 2022.",
|
||||
"thumbnail": "https://modrinth.com/news/default.webp",
|
||||
"date": "2023-01-07T00:00:00.000Z",
|
||||
"link": "https://modrinth.com/news/article/two-years-of-modrinth-history"
|
||||
},
|
||||
{
|
||||
"title": "Creators can now make money on Modrinth!",
|
||||
"summary": "Introducing the Creator Monetization Program allowing creators to earn revenue from their projects.",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -13,6 +13,7 @@
|
||||
"app:build": "turbo run build --filter=@modrinth/app",
|
||||
"app:fix": "turbo run fix --filter=@modrinth/app",
|
||||
"app:intl:extract": "pnpm run --filter=@modrinth/app-frontend intl:extract",
|
||||
"blog:fix": "turbo run fix --filter=@modrinth/blog",
|
||||
"pages:build": "NITRO_PRESET=cloudflare-pages pnpm --filter frontend run build",
|
||||
"build": "turbo run build --continue",
|
||||
"lint": "turbo run lint --continue",
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import * as path from 'path'
|
||||
import fastGlob from 'fast-glob'
|
||||
import { repoPath, toVarName } from './utils'
|
||||
import { glob } from 'glob'
|
||||
|
||||
import { PUBLIC_SRC, PUBLIC_LOCATIONS, ARTICLES_GLOB, COMPILED_DIR } from './blog.config'
|
||||
|
||||
async function checkPublicAssets() {
|
||||
const srcFiles = await fastGlob(['**/*'], { cwd: PUBLIC_SRC, dot: true })
|
||||
const srcFiles = await glob('**/*', { cwd: PUBLIC_SRC, dot: true })
|
||||
let allOk = true
|
||||
for (const target of PUBLIC_LOCATIONS) {
|
||||
for (const relativeFile of srcFiles) {
|
||||
const shouldExist = path.join(target, relativeFile)
|
||||
const shouldExist = path.posix.join(target, relativeFile)
|
||||
try {
|
||||
await fs.access(shouldExist)
|
||||
} catch {
|
||||
@ -26,15 +26,15 @@ async function checkPublicAssets() {
|
||||
}
|
||||
|
||||
async function checkCompiledArticles() {
|
||||
const mdFiles = await fastGlob([ARTICLES_GLOB])
|
||||
const compiledFiles = await fastGlob([`${COMPILED_DIR}/*.ts`])
|
||||
const mdFiles = await glob(ARTICLES_GLOB)
|
||||
const compiledFiles = await glob(`${COMPILED_DIR}/*.ts`)
|
||||
const compiledVarNames = compiledFiles.map((f) => path.basename(f, '.ts'))
|
||||
|
||||
// Check all .md have compiled .ts and .content.ts and the proper public thumbnail
|
||||
for (const file of mdFiles) {
|
||||
const varName = toVarName(path.basename(file, '.md'))
|
||||
const compiledPath = path.join(COMPILED_DIR, varName + '.ts')
|
||||
const contentPath = path.join(COMPILED_DIR, varName + '.content.ts')
|
||||
const compiledPath = path.posix.join(COMPILED_DIR, varName + '.ts')
|
||||
const contentPath = path.posix.join(COMPILED_DIR, varName + '.content.ts')
|
||||
if (!compiledVarNames.includes(varName)) {
|
||||
console.error(`⚠️ Missing compiled article for: ${file} (should be: ${compiledPath})`)
|
||||
process.exit(1)
|
||||
@ -59,7 +59,7 @@ async function checkCompiledArticles() {
|
||||
if (varName === 'index' || varName.endsWith('.content')) continue
|
||||
|
||||
const mdPathGlob = repoPath(`packages/blog/articles/**/${varName.replace(/_/g, '*')}.md`)
|
||||
const found = await fastGlob([mdPathGlob])
|
||||
const found = await glob(mdPathGlob)
|
||||
if (!found.length) {
|
||||
console.error(`❌ Compiled article ${compiled} has no matching markdown source!`)
|
||||
process.exit(1)
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import * as path from 'path'
|
||||
import fg from 'fast-glob'
|
||||
import matter from 'gray-matter'
|
||||
import { md } from '@modrinth/utils'
|
||||
import { minify } from 'html-minifier-terser'
|
||||
import { copyDir, toVarName } from './utils'
|
||||
import RSS from 'rss'
|
||||
import { parseStringPromise } from 'xml2js'
|
||||
import { glob } from 'glob'
|
||||
|
||||
import {
|
||||
ARTICLES_GLOB,
|
||||
@ -24,7 +24,7 @@ async function ensureCompiledDir() {
|
||||
}
|
||||
|
||||
async function hasThumbnail(slug: string): Promise<boolean> {
|
||||
const thumbnailPath = path.join(PUBLIC_SRC, slug, 'thumbnail.webp')
|
||||
const thumbnailPath = path.posix.join(PUBLIC_SRC, slug, 'thumbnail.webp')
|
||||
try {
|
||||
await fs.access(thumbnailPath)
|
||||
return true
|
||||
@ -48,7 +48,7 @@ function getThumbnailUrl(slug: string, hasThumb: boolean): string {
|
||||
async function compileArticles() {
|
||||
await ensureCompiledDir()
|
||||
|
||||
const files = await fg([ARTICLES_GLOB])
|
||||
const files = await glob(ARTICLES_GLOB)
|
||||
console.log(`🔎 Found ${files.length} markdown articles!`)
|
||||
const articleExports: string[] = []
|
||||
const articlesArray: string[] = []
|
||||
@ -75,8 +75,8 @@ async function compileArticles() {
|
||||
|
||||
const slug = frontSlug || path.basename(file, '.md')
|
||||
const varName = toVarName(slug)
|
||||
const exportFile = path.join(COMPILED_DIR, `${varName}.ts`)
|
||||
const contentFile = path.join(COMPILED_DIR, `${varName}.content.ts`)
|
||||
const exportFile = path.posix.join(COMPILED_DIR, `${varName}.ts`)
|
||||
const contentFile = path.posix.join(COMPILED_DIR, `${varName}.content.ts`)
|
||||
const thumbnailPresent = await hasThumbnail(slug)
|
||||
|
||||
const contentTs = `
|
||||
@ -221,7 +221,7 @@ async function deleteDirContents(dir: string) {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true })
|
||||
await Promise.all(
|
||||
entries.map(async (entry) => {
|
||||
const fullPath = path.join(dir, entry.name)
|
||||
const fullPath = path.posix.join(dir, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
await fs.rm(fullPath, { recursive: true, force: true })
|
||||
} else {
|
||||
|
||||
@ -1,56 +1,56 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
import { article as a_new_chapter_for_modrinth_servers } from './a_new_chapter_for_modrinth_servers'
|
||||
import { article as accelerating_development } from './accelerating_development'
|
||||
import { article as becoming_sustainable } from './becoming_sustainable'
|
||||
import { article as capital_return } from './capital_return'
|
||||
import { article as carbon_ads } from './carbon_ads'
|
||||
import { article as creator_monetization } from './creator_monetization'
|
||||
import { article as creator_update } from './creator_update'
|
||||
import { article as creator_updates_july_2025 } from './creator_updates_july_2025'
|
||||
import { article as design_refresh } from './design_refresh'
|
||||
import { article as download_adjustment } from './download_adjustment'
|
||||
import { article as knossos_v2_1_0 } from './knossos_v2_1_0'
|
||||
import { article as licensing_guide } from './licensing_guide'
|
||||
import { article as modpack_changes } from './modpack_changes'
|
||||
import { article as modpacks_alpha } from './modpacks_alpha'
|
||||
import { article as modrinth_app_beta } from './modrinth_app_beta'
|
||||
import { article as modrinth_beta } from './modrinth_beta'
|
||||
import { article as modrinth_servers_beta } from './modrinth_servers_beta'
|
||||
import { article as new_site_beta } from './new_site_beta'
|
||||
import { article as plugins_resource_packs } from './plugins_resource_packs'
|
||||
import { article as pride_campaign_2025 } from './pride_campaign_2025'
|
||||
import { article as redesign } from './redesign'
|
||||
import { article as skins_now_in_modrinth_app } from './skins_now_in_modrinth_app'
|
||||
import { article as two_years_of_modrinth_history } from './two_years_of_modrinth_history'
|
||||
import { article as two_years_of_modrinth } from './two_years_of_modrinth'
|
||||
import { article as whats_modrinth } from './whats_modrinth'
|
||||
import { article as windows_borderless_malware_disclosure } from './windows_borderless_malware_disclosure'
|
||||
import { article as whats_modrinth } from './whats_modrinth'
|
||||
import { article as two_years_of_modrinth } from './two_years_of_modrinth'
|
||||
import { article as two_years_of_modrinth_history } from './two_years_of_modrinth_history'
|
||||
import { article as skins_now_in_modrinth_app } from './skins_now_in_modrinth_app'
|
||||
import { article as redesign } from './redesign'
|
||||
import { article as pride_campaign_2025 } from './pride_campaign_2025'
|
||||
import { article as plugins_resource_packs } from './plugins_resource_packs'
|
||||
import { article as new_site_beta } from './new_site_beta'
|
||||
import { article as modrinth_servers_beta } from './modrinth_servers_beta'
|
||||
import { article as modrinth_beta } from './modrinth_beta'
|
||||
import { article as modrinth_app_beta } from './modrinth_app_beta'
|
||||
import { article as modpacks_alpha } from './modpacks_alpha'
|
||||
import { article as modpack_changes } from './modpack_changes'
|
||||
import { article as licensing_guide } from './licensing_guide'
|
||||
import { article as knossos_v2_1_0 } from './knossos_v2_1_0'
|
||||
import { article as download_adjustment } from './download_adjustment'
|
||||
import { article as design_refresh } from './design_refresh'
|
||||
import { article as creator_updates_july_2025 } from './creator_updates_july_2025'
|
||||
import { article as creator_update } from './creator_update'
|
||||
import { article as creator_monetization } from './creator_monetization'
|
||||
import { article as carbon_ads } from './carbon_ads'
|
||||
import { article as capital_return } from './capital_return'
|
||||
import { article as becoming_sustainable } from './becoming_sustainable'
|
||||
import { article as accelerating_development } from './accelerating_development'
|
||||
import { article as a_new_chapter_for_modrinth_servers } from './a_new_chapter_for_modrinth_servers'
|
||||
|
||||
export const articles = [
|
||||
a_new_chapter_for_modrinth_servers,
|
||||
accelerating_development,
|
||||
becoming_sustainable,
|
||||
capital_return,
|
||||
carbon_ads,
|
||||
creator_monetization,
|
||||
creator_update,
|
||||
creator_updates_july_2025,
|
||||
design_refresh,
|
||||
download_adjustment,
|
||||
knossos_v2_1_0,
|
||||
licensing_guide,
|
||||
modpack_changes,
|
||||
modpacks_alpha,
|
||||
modrinth_app_beta,
|
||||
modrinth_beta,
|
||||
modrinth_servers_beta,
|
||||
new_site_beta,
|
||||
plugins_resource_packs,
|
||||
pride_campaign_2025,
|
||||
redesign,
|
||||
skins_now_in_modrinth_app,
|
||||
two_years_of_modrinth_history,
|
||||
two_years_of_modrinth,
|
||||
whats_modrinth,
|
||||
windows_borderless_malware_disclosure,
|
||||
whats_modrinth,
|
||||
two_years_of_modrinth,
|
||||
two_years_of_modrinth_history,
|
||||
skins_now_in_modrinth_app,
|
||||
redesign,
|
||||
pride_campaign_2025,
|
||||
plugins_resource_packs,
|
||||
new_site_beta,
|
||||
modrinth_servers_beta,
|
||||
modrinth_beta,
|
||||
modrinth_app_beta,
|
||||
modpacks_alpha,
|
||||
modpack_changes,
|
||||
licensing_guide,
|
||||
knossos_v2_1_0,
|
||||
download_adjustment,
|
||||
design_refresh,
|
||||
creator_updates_july_2025,
|
||||
creator_update,
|
||||
creator_monetization,
|
||||
carbon_ads,
|
||||
capital_return,
|
||||
becoming_sustainable,
|
||||
accelerating_development,
|
||||
a_new_chapter_for_modrinth_servers,
|
||||
]
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
"fix": "jiti ./compile.ts && eslint . --fix && prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^9.0.0",
|
||||
"@types/html-minifier-terser": "^7.0.2",
|
||||
"@types/rss": "^0.0.32",
|
||||
"@types/xml2js": "^0.4.14",
|
||||
@ -19,7 +20,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"fast-glob": "^3.3.3",
|
||||
"glob": "^10.2.7",
|
||||
"gray-matter": "^4.0.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"rss": "^1.2.2",
|
||||
|
||||
@ -8,7 +8,7 @@ export function getRepoRoot(): string {
|
||||
}
|
||||
|
||||
export function repoPath(...segments: string[]): string {
|
||||
return path.join(getRepoRoot(), ...segments)
|
||||
return path.posix.join(getRepoRoot(), ...segments)
|
||||
}
|
||||
|
||||
export async function copyDir(
|
||||
@ -20,8 +20,8 @@ export async function copyDir(
|
||||
await fs.mkdir(dest, { recursive: true })
|
||||
const entries = await fs.readdir(src, { withFileTypes: true })
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.join(src, entry.name)
|
||||
const destPath = path.join(dest, entry.name)
|
||||
const srcPath = path.posix.join(src, entry.name)
|
||||
const destPath = path.posix.join(dest, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
await copyDir(srcPath, destPath, logFn)
|
||||
} else if (entry.isFile()) {
|
||||
|
||||
@ -1,28 +1,32 @@
|
||||
import type { Stage } from '../types/stage'
|
||||
import modpackPermissionsStage from './modpack-permissions-stage'
|
||||
import categories from './stages/categories'
|
||||
import copyright from './stages/copyright'
|
||||
import reupload from './stages/reupload'
|
||||
import description from './stages/description'
|
||||
import gallery from './stages/gallery'
|
||||
import links from './stages/links'
|
||||
import ruleFollowing from './stages/rule-following'
|
||||
import sideTypes from './stages/side-types'
|
||||
import slug from './stages/slug'
|
||||
import summary from './stages/summary'
|
||||
import title from './stages/title'
|
||||
import titleSlug from './stages/title-slug'
|
||||
import versions from './stages/versions'
|
||||
import license from './stages/license'
|
||||
import undefinedProject from './stages/undefined-project'
|
||||
import statusAlerts from './stages/status-alerts'
|
||||
|
||||
export default [
|
||||
title,
|
||||
slug,
|
||||
titleSlug,
|
||||
summary,
|
||||
description,
|
||||
links,
|
||||
license,
|
||||
categories,
|
||||
sideTypes,
|
||||
gallery,
|
||||
versions,
|
||||
copyright,
|
||||
reupload,
|
||||
ruleFollowing,
|
||||
modpackPermissionsStage,
|
||||
statusAlerts,
|
||||
undefinedProject,
|
||||
] as ReadonlyArray<Stage>
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
## Misuse of Tags
|
||||
|
||||
Per section 5.1 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous), it is important that the metadata of your projects is accurate. Including that selected tags honestly represent your project.
|
||||
Per section 5.1 of %RULES%, it is important that the metadata of your projects is accurate. Including that selected tags honestly represent your project.
|
||||
|
||||
@ -0,0 +1 @@
|
||||
It looks like the Optimization tag is not accurate for this project.
|
||||
@ -0,0 +1,5 @@
|
||||
Currently, your pack has multiple Resolutions selected, in most cases, this will misrepresent your project.
|
||||
For a brief rundown of how this works:
|
||||
Vanilla Minecraft textures like blocks and items have a width and height of 16 pixels.
|
||||
This means that packs with textures the same size as the default are considered 16x.
|
||||
Some packs make the resolution of textures smaller than the default, such as 8x, and some bigger, such as 32x.
|
||||
@ -0,0 +1,2 @@
|
||||
**Featured Tags:** %PROJECT_CATEGORIES% \
|
||||
**Additional Tags:** %PROJECT_ADDITIONAL_CATEGORIES%
|
||||
@ -0,0 +1 @@
|
||||
**License id:** %PROJECT_LICENSE_ID% \
|
||||
@ -0,0 +1 @@
|
||||
**License Link:** %PROJECT_LICENSE_URL%
|
||||
@ -0,0 +1,4 @@
|
||||
**Discord:** %PROJECT_DISCORD_URL% \
|
||||
**Issues:** %PROJECT_ISSUES_URL% \
|
||||
**Source:** %PROJECT_SOURCE_URL% \
|
||||
**Wiki:** %PROJECT_WIKI_URL%
|
||||
@ -0,0 +1 @@
|
||||
> **{PLATFORM}:** {URL}
|
||||
@ -0,0 +1 @@
|
||||
<u>**Donation Links:**</u>
|
||||
@ -0,0 +1 @@
|
||||
**Applying for:** `%PROJECT_REQUESTED_STATUS%`
|
||||
@ -0,0 +1,2 @@
|
||||
**Summary:**
|
||||
`%PROJECT_SUMMARY%`
|
||||
@ -0,0 +1,4 @@
|
||||
**Title:** %PROJECT_TITLE% \
|
||||
**Slug:** `%PROJECT_SLUG%`
|
||||
|
||||
**Title issues?**
|
||||
@ -1,7 +1,6 @@
|
||||
## Insufficient Description
|
||||
|
||||
Per section 2.1 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#general-expectations) your project's Description should clearly inform the reader of the content, purpose, and appeal of your project.
|
||||
Per section 2.1 of %RULES% your project's Description should clearly inform the reader of the content, purpose, and appeal of your project.
|
||||
|
||||
Currently, it looks like there are some missing details.
|
||||
|
||||
> %EXPLAINER%
|
||||
%EXPLAINER%
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
## No English Description
|
||||
|
||||
Per section 2.2 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#accessibility) a project's Summary and Description must be in English, unless meant exclusively for non-English use, such as translations.
|
||||
Per section 2.2 of %RULES% a project's Summary and Description must be in English, unless meant exclusively for non-English use, such as translations.
|
||||
|
||||
You may include your non-English Description if you would like but we ask that you also add an English translation of the Description to your Description page, if you would like to use an online translator to do this, we recommend [DeepL](https://www.deepl.com/translator).
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
## Description Accessibility
|
||||
|
||||
Per section 2 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#clear-and-honest-function) your description must be plainly readable and accessible.
|
||||
Per section 2 of %RULES% your description must be plainly readable and accessible.
|
||||
|
||||
Using non-standard text characters like Zalgo or "fancy text" in place of text anywhere in your project, including the Description, Summary, or Title can make your project pages inaccessible.
|
||||
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
## Unfinished Description
|
||||
|
||||
It looks like your project Description is still a WIP (Work In Progress).
|
||||
|
||||
> %REASON%
|
||||
|
||||
Please remember to submit only when ready, as it is important your project meets the requirements of Section 2.1 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#general-expectations), if you have any questions on this feel free to reach out!
|
||||
It looks like your project Description is still a Work In Progress.
|
||||
Please remember to submit only when ready, as it is important your project meets the requirements of Section 2.1 of %RULES%.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
## Insufficient Gallery Images
|
||||
|
||||
We ask that projects like yours show off their content using images in the Gallery, or optionally in the Description, in order to effectively and clearly inform users of its content per section 2.1 of [Modrinth's content rules](https://modrinth.com/legal/rules#general-expectations).
|
||||
We ask that projects like yours show off their content using images in the Gallery, or optionally in the Description, in order to effectively and clearly inform users of its content per section 2.1 of %RULES%.
|
||||
Keep in mind that you should:
|
||||
|
||||
- Set a featured image that best represents your project.
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
## Unrelated Gallery Images
|
||||
|
||||
Per section 5.5 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous) any images in your project's Gallery must be relevant to the project and also include a Title.
|
||||
Per section 5.5 of %RULES% any images in your project's Gallery must be relevant to the project and also include a Title.
|
||||
|
||||
@ -0,0 +1 @@
|
||||
If this is a project-specific License you've written yourself, you must host the full License in a way that makes it publicly available, for instance, in a public source repository on a platform like GitHub.
|
||||
@ -0,0 +1,4 @@
|
||||
## Invalid License Link
|
||||
|
||||
It's important that your project's License link is accurate and leads directly to a valid license for this content.
|
||||
Your current link: `%PROJECT_LICENSE_URL%` does not appear to lead to a valid license for this project, or it is not publicly accessable.
|
||||
@ -0,0 +1,5 @@
|
||||
## No Source Code Provided
|
||||
|
||||
Your project's license of `%PROJECT_LICENSE_NAME%`, requires source disclosure.
|
||||
Consider adding a Source link to your project's repository, or including a Sources file for each version as an Additional File.
|
||||
Keep in mind this may be a requirement of the source work's licensing, which must be abided per section 4 of %RULES%.
|
||||
4
packages/moderation/data/messages/license/no_source.md
Normal file
4
packages/moderation/data/messages/license/no_source.md
Normal file
@ -0,0 +1,4 @@
|
||||
## No Source Code Provided
|
||||
|
||||
Your project's license of `%PROJECT_LICENSE_NAME%`, requires source disclosure.
|
||||
Consider adding a Source link to your project's repository, or including a Sources file for each version as an Additional File. You may also want to refer to %LICENSING_GUIDE% if you wish to select a different License, remember to make sure your selected License is consistent with the license in your project's files as well.
|
||||
@ -1,3 +1,4 @@
|
||||
## Misuse of External Resources
|
||||
## Misuse of Links
|
||||
|
||||
Per section 5.4 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous) all links must lead to correctly labeled publicly available resources that are directly related to your project.
|
||||
Per section 5.4 of %RULES% all %PROJECT_LINKS_FLINK% must lead to correctly labeled publicly available resources that are directly related to your project.
|
||||
Currently it looks like your %MISUSED_LINKS% link(s) are misused or incorrectly labeled.
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
## Unreachable Links
|
||||
|
||||
Per section 5.4 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous) all links must lead to correctly labeled publicly available resources that are directly related to your project.
|
||||
|
||||
Currently, your %LINK% link is inaccessible!
|
||||
@ -1,5 +0,0 @@
|
||||
## Unreachable Links
|
||||
|
||||
Per section 5.4 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous) all links must lead to correctly labeled publicly available resources that are directly related to your project.
|
||||
|
||||
Currently, your Source link directs to a Page Not Found error, likely because your repository is private, make sure to make your repository public before resubmitting your project!
|
||||
@ -0,0 +1 @@
|
||||
Currently, your Discord link directs to an invalid invite, likely because your invite has expired. Make sure to set your invite link to permanent with unlimited uses before resubmitting your project.
|
||||
@ -0,0 +1 @@
|
||||
Currently, your Source link directs to a Page Not Found error, likely because your repository is private. Make sure to set your repository to public before resubmitting your project.
|
||||
@ -0,0 +1,3 @@
|
||||
## Unreachable Links
|
||||
|
||||
Per section 5.4 of %RULES% all %PROJECT_LINKS_FLINK% must lead to correctly labeled publicly available resources that are directly related to your project.
|
||||
4
packages/moderation/data/messages/reupload/fork.md
Normal file
4
packages/moderation/data/messages/reupload/fork.md
Normal file
@ -0,0 +1,4 @@
|
||||
## Forks and Reuploads
|
||||
|
||||
Per section 4 of %RULES%, please provide proof that this project is both license-abiding and significantly divergent from the source work.
|
||||
Alternatively, please provide proof of your explicit permission from the author of the source work to distribute this content on Modrinth.
|
||||
@ -0,0 +1,4 @@
|
||||
## Insufficient Fork
|
||||
|
||||
This project does not appear to significantly diverge from the source work, or does not abide by the license of the source work as required by section 4 of %RULES%.
|
||||
Please provide proof of your explicit permission to distribute this project from the creator(s) of the source work.
|
||||
@ -0,0 +1,5 @@
|
||||
## Proof of permissions
|
||||
|
||||
This project appears to contain content from other creators.
|
||||
Per section 4 of %RULES%, we ask that you provide proof of your permission to distribute this content, or derivatives of this content in your project on Modrinth.
|
||||
Either implicit permission abiding by the terms of the content's license(s) or explicit permission from the original creator of the content.
|
||||
@ -1,7 +1,5 @@
|
||||
## Reuploads are forbidden
|
||||
|
||||
This project appears to contain content from %ORIGINAL_PROJECT% by %ORIGINAL_AUTHOR%.
|
||||
|
||||
Per section 4 of [Modrinth's Content Rules](https://modrinth.com/legal/rules) this is strictly forbidden.
|
||||
|
||||
If you believe this is an error, or you can verify you are the creator and rightful owner of this content please let us know. Otherwise, we ask that you **do not resubmit this project**.`,
|
||||
Per section 4 of %RULES% this is strictly forbidden.
|
||||
If you believe this is an error, or you can verify you are the creator and rightful owner of this content please let us know. Otherwise, we ask that you **do not resubmit this project**.
|
||||
@ -1,5 +1 @@
|
||||
# Does not follow content rules
|
||||
|
||||
Our moderators have determined that your project does not follow Modrinth's Content Rules, and has been rejected.
|
||||
|
||||
%MESSAGE%
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
## Environment Information
|
||||
|
||||
Per section 5.1 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous), it is important that the metadata of your projects is accurate, including whether the project runs on the client or server side.
|
||||
Per section 5.1 of %RULES%, it is important that the metadata of your projects is accurate, including whether the project runs on the client or server side.
|
||||
|
||||
For a brief rundown of how this works:
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
## Incorrect Environment Information
|
||||
|
||||
Per section 5.1 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous), it is important that the metadata of your projects is accurate, including whether the project runs on the client or server side.
|
||||
Per section 5.1 of %RULES%, it is important that the metadata of your projects is accurate, including whether the project runs on the client or server side.
|
||||
|
||||
For a brief rundown of how this works:
|
||||
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
## Misuse of Slug
|
||||
|
||||
Per section 5.2 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous) your project slug (URL) must accurately represent your project.
|
||||
Per section 5.2 of %RULES% must accurately represent your project.
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
---
|
||||
|
||||
## Account Issues Indicated
|
||||
|
||||
We're sorry to hear you're having trouble accessing your accounts, unfortunately, our moderation team is unable to assist with account-related issues.
|
||||
Before resubmitting your project, %SUPPORT%.
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
|
||||
Unfortunately, our AutoMod cannot read your project's Description or your messages to moderation.
|
||||
AutoMod will warn both you and our Moderation Staff about potential issues, but if you've already followed the necessary steps these warnings can safely be ignored.
|
||||
Note that if your project is being rejected by AutoMod this means your project has content that can not be included in your modpack and must be removed before resubmission, including deleting versions of your modpack that include the content.
|
||||
5
packages/moderation/data/messages/status-alerts/fixed.md
Normal file
5
packages/moderation/data/messages/status-alerts/fixed.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
|
||||
## Corrections Applied
|
||||
|
||||
I've gone ahead and corrected the issues listed above so your project can be Approved.
|
||||
@ -0,0 +1,8 @@
|
||||
---
|
||||
|
||||
## Private Use
|
||||
|
||||
Under normal circumstances, your project would be rejected due to the issues listed above.
|
||||
However, since your project is not intended for for public use, these requirements will be waived and your project will be unlisted. This means it will remain accessible through a direct link without appearing in public search results, allowing you to share it privately.
|
||||
If you're okay with this, or submitted your project to be unlisted already, than no further action is necessary.
|
||||
If you would like to publish your project publicly, please address all moderation concerns before resubmitting this project.
|
||||
@ -1,6 +1,6 @@
|
||||
## Insufficient Summary
|
||||
|
||||
Per section 5.3 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous) your Summary can not include any extra formatting such as lists, or links.
|
||||
Per section 5.3 of %RULES% your Summary can not include any extra formatting such as lists, or links.
|
||||
|
||||
Your project summary should provide a brief overview of your project that informs and entices users.
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
## Insufficient Summary
|
||||
|
||||
Per section 5.3 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous) your project summary should provide a brief overview of your project that informs and entices users.
|
||||
Per section 5.3 of %RULES% your project summary should provide a brief overview of your project that informs and entices users.
|
||||
|
||||
This is the first thing most people will see about your mod other than the Logo, so it's important it be accurate, reasonably detailed, and exciting.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
## No English Summary
|
||||
|
||||
Per section 2.2 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#accessibility) a project's Summary and Description must be in English, unless meant exclusively for non-English use, such as translations.
|
||||
Per section 2.2 of %RULES% a project's Summary and Description must be in English, unless meant exclusively for non-English use, such as translations.
|
||||
|
||||
You may include your non-English Summary but we ask that you also add an English translation.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
## Insufficient Summary
|
||||
|
||||
Per section 5.3 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous) your Summary can not be the same as your project's Title.
|
||||
Per section 5.3 of %RULES% your Summary can not be the same as your project's Title.
|
||||
|
||||
Your project summary should provide a brief overview of your project that informs and entices users.
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
## Project Title
|
||||
## Minecraft Project Names
|
||||
|
||||
Projects must not use Minecraft's branding or include "Minecraft" as a significant part of the title.
|
||||
|
||||
The title of your project may be confusingly similar to the game, and we encourage you to change your title to avoid a potential violation of Minecraft's Usage Guidelines.
|
||||
|
||||
Your project's current Name of `%PROJECT_TITLE%` may be confusingly similar to, or imply association with, the game Minecraft. We encourage you to change your project's [Name](%PROJECT_SETTINGS_LINK%) to avoid a potential violation of Minecraft's Usage Guidelines.
|
||||
Abbreviations like "MC" or elaborate titles that do not make the name Minecraft a significant portion of the name are okay.
|
||||
When editing your project's Name, remember to update its [URL](%PROJECT_SETTINGS_LINK%) to match.
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
You may reference the source work in the Description of your fork, however, you must do so in a way that is unlikely to cause confusion or imply association with or endorsement from the author of the source work.
|
||||
Additionally, per section 4 of %RULES%, we ask that you ensure your project's Name and Branding abide by the license of the source work.
|
||||
@ -0,0 +1 @@
|
||||
You may reference the projects that make up the focus point of your modpack, however, you must do so in a way that is unlikely to cause confusion or imply association with or endorsement from the author of that content.
|
||||
@ -1,3 +1,5 @@
|
||||
## Project Branding
|
||||
|
||||
Per section 1.8 of [Modrinth's Content Rules](https://modrinth.com/legal/rules) we ask that you change your project title and other relevant branding to avoid causing confusion or implying association with existing projects.
|
||||
Per section 1.8 of %RULES%, your project or its branding must not imply association or be easily confused with any other person or organization.
|
||||
We ask that you change your project's [Name](%PROJECT_SETTINGS_LINK%) and other relevant branding to avoid causing confusion or implying association with existing projects or individuals.
|
||||
When editing your project's Name, remember to update its [URL](%PROJECT_SETTINGS_LINK%) to match.
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
## Misuse of Title
|
||||
## Misuse of Project Name
|
||||
|
||||
Per section 5.2 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous) we ask that you limit the title to just the name of your project.
|
||||
|
||||
Additional information, such as themes, tags, supported versions or loaders, etc. should be saved for the Summary or Description.
|
||||
|
||||
When changing your project title, remember to also ensure that your project slug (URL) matches and accurately represents your project.
|
||||
Per section 5.2 of %RULES%, your project's [Name](%PROJECT_SETTINGS_LINK%) should not include unnecessary information such as loaders, themes, tags, or versions.
|
||||
Your project's current Name of `%PROJECT_TITLE%` appears to contain extra information.
|
||||
We ask that you remove all additional information from the [Name](%PROJECT_SETTINGS_LINK%). Instead, consider including this in your project's Summary or Description, or as a part of its relevant metadata.
|
||||
When editing your project's Name, remember to update its [URL](%PROJECT_SETTINGS_LINK%) to match.
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
## No Versions
|
||||
|
||||
It looks like all versions of your project have been deleted, meaning that our Moderation Team cannot finish reviewing your project.
|
||||
Please resubmit your project once you've uploaded a new version.
|
||||
@ -0,0 +1,5 @@
|
||||
## Unsupported Project
|
||||
|
||||
Per section 5.7 of [Modrinth's Content Rules](https://modrinth.com/legal/rules), Modrinth does not support uploading multiple variations of your project as Additional files.
|
||||
Having alternate versions of your content on the same project will hurt the functionality of the Modrinth App and other supported launchers as it would prevent users from updating your content, and may make it harder for your users to find the content they want.
|
||||
We ask that you upload each alternate version of your project as a new project, ensuring that all users will be able to access and easily find your content.
|
||||
@ -0,0 +1,5 @@
|
||||
## Unsupported Project
|
||||
|
||||
Modrinth does not support uploading projects that have unnecessary or extraneous installation steps.
|
||||
Having alternate versions of your content on the same project will hurt the functionality of the Modrinth App and other supported launchers as it would prevent users from updating your content, and may make it harder for your users to find the content they want.
|
||||
We ask that you upload each alternate version of your project as a new project, ensuring that all users will be able to access and easily find your content.
|
||||
@ -0,0 +1,6 @@
|
||||
## Unsupported Project
|
||||
|
||||
Modrinth does not support uploading multiple variations of your project as separate versions.
|
||||
New Versions of your project should strictly be a linear upgrade of the same content.
|
||||
Having alternate versions of your content on the same project will hurt the functionality of the Modrinth App and other supported launchers as it would prevent users from updating your content, and may make it harder for your users to find the content they want.
|
||||
We ask that you upload each alternate version of your project as a new project, ensuring that all users will be able to access and easily find your content.
|
||||
@ -0,0 +1,4 @@
|
||||
## Incorrect Additional Files
|
||||
|
||||
Per section 5.7 of [Modrinth's Content Rules](https://modrinth.com/legal/rules) the additional files section should only be used for specific designated purposes such as a `Sources.jar`.
|
||||
To ensure a smooth experience for you and your users, please upload each alternate version of your modpack as its own Modpack project, thank you.
|
||||
@ -0,0 +1,6 @@
|
||||
## Unsupported Project
|
||||
|
||||
Modrinth does not support uploading multiple variations of your project as separate versions.
|
||||
New Versions of your project should strictly be a linear upgrade of the same content.
|
||||
Having alternate versions of your content on the same project will hurt the functionality of the Modrinth App and other supported launchers as it would prevent users from updating your content, and may make it harder for your users to find the content they want.
|
||||
To ensure a smooth experience for you and your users, please upload each alternate version of your modpack as its own Modpack project, thank you.
|
||||
@ -0,0 +1,5 @@
|
||||
## Incorrect Additional Files
|
||||
|
||||
Per section 5.7 of [Modrinth's Content Rules](https://modrinth.com/legal/rules) the additional files section should only be used for specific designated purposes such as a `Sources.jar`.
|
||||
Modrinth does not support the upload of modpacks in the `.zip` format, as this may cause issues for Modrinth users or distribute copyrighted content without the proper permissions.
|
||||
If you would like to upload a server-specific version of your modpack, consider creating a separate Modpack project.
|
||||
@ -0,0 +1,5 @@
|
||||
## Broken Version
|
||||
|
||||
It looks like you've uploaded multiple files with the same name to the same version. This triggers a bug that produces duplicate primary file entries.
|
||||
This may cause issues for users and creators as it would prevent Modpacks that include this content from functioning in the Modrinth App and other launchers.
|
||||
We ask that you delete and reupload all applicable versions. Be sure to only upload one file to each version before resubmission.
|
||||
@ -1,7 +0,0 @@
|
||||
## Incorrect Use of Additional Files
|
||||
|
||||
It looks like you've uploaded multiple `mod.jar` files to one Version as Additional Files. Per section 5.7 of [Modrinth's Content Rules](https://modrinth.com/legal/rules#miscellaneous) each Version of your project must include only one `mod.jar` that corresponds to its respective Minecraft and loader versions.
|
||||
|
||||
This allows users to easily find and download the file they need for the version they're on with ease. The Additional Files feature can be used for things like a `Sources.jar`.
|
||||
|
||||
Please upload each version of your mod separately, thank you.
|
||||
@ -0,0 +1,5 @@
|
||||
## Incorrect Use of Additional Files
|
||||
|
||||
It looks like you've uploaded multiple primary files to one Version as Additional Files. Per section 5.7 of %RULES% each Version of your project must include only one primary file that corresponds to its respective Minecraft and loader versions.
|
||||
This allows users to easily find and download the content they need for their game profile with ease. The Additional Files feature can be used for things like a `Sources.jar`.
|
||||
Please upload each version of your project separately, thank you.
|
||||
@ -0,0 +1,3 @@
|
||||
## Data Packs on Modrinth
|
||||
|
||||
It looks like you've selected loaders for your Data Pack that are causing it to be marked as a different project type. Data Packs must only be uploaded with the "Data Pack" loader selected. Please re-upload all versions of your data pack and make sure to only select "Data Pack" as the loader.
|
||||
@ -1,3 +1,5 @@
|
||||
## Modpacks on Modrinth
|
||||
|
||||
It looks like you've uploaded your Modpack as a `.zip`, unfortunately, this is invalid and is why your project type is "Mod". I recommend taking a look at our support page about [Modrinth Modpacks](https://support.modrinth.com/en/articles/8802250-modpacks-on-modrinth), and once you're ready feel free to resubmit your project as a `.mrpack`. Don't forget to delete the old files from your Versions!
|
||||
It looks like you've uploaded your Modpack as a `.zip`, unfortunately, this is invalid and is why your project type is "Mod". Please refer to our support article to learn more about %MODPACKS_ON_MODRINTH%.
|
||||
Once you're ready feel free to resubmit your project as a `.mrpack`.
|
||||
Don't forget to delete the old files from your %PROJECT_VERSIONS_FLINK%!
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
## Excessive File Size
|
||||
|
||||
This project appears to include libs or dependencies, unnecessarily redistributing their entire contents.
|
||||
This is often due to an error in project structure or compilation, and in some cases, may violate the copyrights or licensing agreements of these libraries.
|
||||
We ask that you remove all unnecessary files, assets, and code from your project before resubmission.
|
||||
@ -0,0 +1,4 @@
|
||||
## Vanilla Assets
|
||||
|
||||
Your resource pack currently includes an excessive amount of unmodified assets from vanilla Minecraft.
|
||||
Please remove these from your pack and ensure your project only contains original assets created by you, thank you.
|
||||
@ -1,15 +1,22 @@
|
||||
import type { ModerationModpackPermissionApprovalType, Project } from '@modrinth/utils'
|
||||
import type { Stage } from '../types/stage'
|
||||
import { BoxIcon } from '@modrinth/assets'
|
||||
import { PackageOpenIcon } from '@modrinth/assets'
|
||||
|
||||
export default {
|
||||
id: 'modpack-permissions',
|
||||
title: 'Modpack Permissions',
|
||||
icon: BoxIcon,
|
||||
icon: PackageOpenIcon,
|
||||
// Replace me please.
|
||||
guidance_url: 'https://docs.modrinth.com/moderation/modpack-permissions',
|
||||
guidance_url:
|
||||
'https://www.notion.so/Content-Moderation-Cheat-Sheets-22d5ee711bf081a4920ef08879fe6bf5?source=copy_link#22d5ee711bf08116bd8bc1186f357062',
|
||||
shouldShow: (project: Project) => project.project_type === 'modpack',
|
||||
actions: [],
|
||||
actions: [
|
||||
{
|
||||
id: 'button',
|
||||
type: 'button',
|
||||
label: 'This dummy button must be present or the stage will not appear.',
|
||||
},
|
||||
],
|
||||
} as Stage
|
||||
|
||||
export const finalPermissionMessages: Record<
|
||||
@ -18,7 +25,7 @@ export const finalPermissionMessages: Record<
|
||||
> = {
|
||||
yes: undefined,
|
||||
'with-attribution-and-source': undefined,
|
||||
'with-attribution': `The following content has attribution requirements, meaning that you must link back to the page where you originally found this content in your modpack description or version changelog (e.g. linking a mod's CurseForge page if you got it from CurseForge):`,
|
||||
'with-attribution': `The following content has attribution requirements, meaning that you must link back to the page where you originally found this content in your Modpack's description or version changelog (e.g. linking a mod's CurseForge page if you got it from CurseForge):`,
|
||||
no: 'The following content is not allowed in Modrinth modpacks due to licensing restrictions. Please contact the author(s) directly for permission or remove the content from your modpack:',
|
||||
'permanent-no': `The following content is not allowed in Modrinth modpacks, regardless of permission obtained. This may be because it breaks Modrinth's content rules or because the authors, upon being contacted for permission, have declined. Please remove the content from your modpack:`,
|
||||
unidentified: `The following content could not be identified. Please provide proof of its origin along with proof that you have permission to include it:`,
|
||||
|
||||
@ -3,21 +3,53 @@ import type { ButtonAction } from '../../types/actions'
|
||||
import { TagsIcon } from '@modrinth/assets'
|
||||
|
||||
const categories: Stage = {
|
||||
title: "Are the project's tags/categories accurate?",
|
||||
title: "Are the project's tags accurate?",
|
||||
id: 'tags',
|
||||
icon: TagsIcon,
|
||||
guidance_url: 'https://modrinth.com/legal/rules#miscellaneous',
|
||||
navigate: '/settings/tags',
|
||||
shouldShow: (project) =>
|
||||
project.categories.length > 0 || project.additional_categories.length > 0,
|
||||
text: async () => {
|
||||
return (await import('../messages/checklist-text/categories.md?raw')).default
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
id: 'categories_inaccurate',
|
||||
type: 'button',
|
||||
label: 'Inaccurate',
|
||||
weight: 10,
|
||||
weight: 700,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'low',
|
||||
message: async () => (await import('../messages/categories/inaccurate.md?raw')).default,
|
||||
disablesActions: ['categories_optimization_misused', 'categories_resolutions_misused'],
|
||||
} as ButtonAction,
|
||||
{
|
||||
id: 'categories_optimization_misused',
|
||||
type: 'button',
|
||||
label: 'Optimization',
|
||||
weight: 701,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'low',
|
||||
shouldShow: (project) => project.categories.includes('optimization'),
|
||||
message: async () =>
|
||||
(await import('../messages/categories/inaccurate.md?raw')).default +
|
||||
(await import('../messages/categories/optimization_misused.md?raw')).default,
|
||||
disablesActions: ['categories_inaccurate', 'categories_resolutions_misused'],
|
||||
} as ButtonAction,
|
||||
{
|
||||
id: 'categories_resolutions_misused',
|
||||
type: 'button',
|
||||
label: 'Resolutions',
|
||||
weight: 702,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'low',
|
||||
shouldShow: (project) => project.project_type === 'resourcepack',
|
||||
message: async () =>
|
||||
(await import('../messages/categories/inaccurate.md?raw')).default +
|
||||
(await import('../messages/categories/resolutions_misused.md?raw')).default,
|
||||
disablesActions: ['categories_inaccurate', 'categories_optimization_misused'],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
import type { Stage } from '../../types/stage'
|
||||
import type { ButtonAction } from '../../types/actions'
|
||||
import { CopyrightIcon } from '@modrinth/assets'
|
||||
|
||||
const copyright: Stage = {
|
||||
title: 'Does the author have proper permissions to post this project?',
|
||||
id: 'copyright',
|
||||
icon: CopyrightIcon,
|
||||
guidance_url: 'https://modrinth.com/legal/rules',
|
||||
actions: [
|
||||
{
|
||||
id: 'copyright_reupload',
|
||||
type: 'button',
|
||||
label: 'Re-upload',
|
||||
weight: 10,
|
||||
suggestedStatus: 'rejected',
|
||||
severity: 'high',
|
||||
message: async () => (await import('../messages/copyright/reupload.md?raw')).default,
|
||||
relevantExtraInput: [
|
||||
{
|
||||
label: 'What is the title of the original project?',
|
||||
variable: 'ORIGINAL_PROJECT',
|
||||
required: true,
|
||||
suggestions: ['Vanilla Tweaks'],
|
||||
},
|
||||
{
|
||||
label: 'What is the author of the original project?',
|
||||
variable: 'ORIGINAL_AUTHOR',
|
||||
required: true,
|
||||
suggestions: ['Vanilla Tweaks Team'],
|
||||
},
|
||||
],
|
||||
} as ButtonAction,
|
||||
],
|
||||
}
|
||||
|
||||
export default copyright
|
||||
@ -3,7 +3,7 @@ import type { ButtonAction } from '../../types/actions'
|
||||
import { LibraryIcon } from '@modrinth/assets'
|
||||
|
||||
const description: Stage = {
|
||||
title: "Is the project's description sufficient?",
|
||||
title: 'Is the description sufficient, accurate, and accessible?',
|
||||
id: 'description',
|
||||
icon: LibraryIcon,
|
||||
guidance_url: 'https://modrinth.com/legal/rules#general-expectations',
|
||||
@ -13,7 +13,7 @@ const description: Stage = {
|
||||
id: 'description_insufficient',
|
||||
type: 'button',
|
||||
label: 'Insufficient (custom)',
|
||||
weight: 10,
|
||||
weight: 400,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'medium',
|
||||
message: async () => (await import('../messages/description/insufficient.md?raw')).default,
|
||||
@ -30,7 +30,7 @@ const description: Stage = {
|
||||
id: 'description_insufficient_packs',
|
||||
type: 'button',
|
||||
label: 'Insufficient',
|
||||
weight: 10,
|
||||
weight: 401,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'medium',
|
||||
shouldShow: (project) => project.project_type === 'modpack',
|
||||
@ -41,7 +41,7 @@ const description: Stage = {
|
||||
id: 'description_insufficient_projects',
|
||||
type: 'button',
|
||||
label: 'Insufficient',
|
||||
weight: 10,
|
||||
weight: 401,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'medium',
|
||||
shouldShow: (project) => project.project_type !== 'modpack',
|
||||
@ -52,7 +52,7 @@ const description: Stage = {
|
||||
id: 'description_non_english',
|
||||
type: 'button',
|
||||
label: 'Non-english',
|
||||
weight: 10,
|
||||
weight: 402,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'medium',
|
||||
message: async () => (await import('../messages/description/non-english.md?raw')).default,
|
||||
@ -61,23 +61,16 @@ const description: Stage = {
|
||||
id: 'description_unfinished',
|
||||
type: 'button',
|
||||
label: 'Unfinished',
|
||||
weight: 10,
|
||||
weight: 403,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'low',
|
||||
message: async () => (await import('../messages/description/unfinished.md?raw')).default,
|
||||
relevantExtraInput: [
|
||||
{
|
||||
label: 'Please specify the reason the description appears unfinished.',
|
||||
variable: 'REASON',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
} as ButtonAction,
|
||||
{
|
||||
id: 'description_headers_as_body',
|
||||
type: 'button',
|
||||
label: 'Headers as body text',
|
||||
weight: 10,
|
||||
weight: 404,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'low',
|
||||
message: async () => (await import('../messages/description/headers-as-body.md?raw')).default,
|
||||
@ -86,7 +79,7 @@ const description: Stage = {
|
||||
id: 'description_image_only',
|
||||
type: 'button',
|
||||
label: 'Image-only',
|
||||
weight: 10,
|
||||
weight: 405,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'medium',
|
||||
message: async () => (await import('../messages/description/image-only.md?raw')).default,
|
||||
@ -95,7 +88,7 @@ const description: Stage = {
|
||||
id: 'description_non_standard_text',
|
||||
type: 'button',
|
||||
label: 'Non-standard text',
|
||||
weight: 10,
|
||||
weight: 406,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'medium',
|
||||
message: async () =>
|
||||
|
||||
@ -13,7 +13,7 @@ const gallery: Stage = {
|
||||
id: 'gallery_insufficient',
|
||||
type: 'button',
|
||||
label: 'Insufficient',
|
||||
weight: 10,
|
||||
weight: 900,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'low',
|
||||
message: async () => (await import('../messages/gallery/insufficient.md?raw')).default,
|
||||
@ -22,7 +22,7 @@ const gallery: Stage = {
|
||||
id: 'gallery_not_relevant',
|
||||
type: 'button',
|
||||
label: 'Not relevant',
|
||||
weight: 10,
|
||||
weight: 901,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'low',
|
||||
message: async () => (await import('../messages/gallery/not-relevant.md?raw')).default,
|
||||
|
||||
100
packages/moderation/data/stages/license.ts
Normal file
100
packages/moderation/data/stages/license.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import { BookTextIcon } from '@modrinth/assets'
|
||||
import type { Stage } from '../../types/stage'
|
||||
|
||||
const licensesNotRequiringSource: string[] = [
|
||||
'LicenseRef-All-Rights-Reserved',
|
||||
'Apache-2.0',
|
||||
'BSD-2-Clause',
|
||||
'BSD-3-Clause',
|
||||
'CC0-1.0',
|
||||
'CC-BY-4.0',
|
||||
'CC-BY-SA-4.0',
|
||||
'CC-BY-NC-4.0',
|
||||
'CC-BY-NC-SA-4.0',
|
||||
'CC-BY-ND-4.0',
|
||||
'CC-BY-NC-ND-4.0',
|
||||
'ISC',
|
||||
'MIT',
|
||||
'Zlib',
|
||||
]
|
||||
|
||||
const licenseStage: Stage = {
|
||||
title: 'Is this license and link valid?',
|
||||
text: async (project) => {
|
||||
let text = ''
|
||||
text += (await import('../messages/checklist-text/license/id.md?raw')).default
|
||||
if (project.license.url)
|
||||
text += (await import('../messages/checklist-text/license/link.md?raw')).default
|
||||
|
||||
return text
|
||||
},
|
||||
id: 'license',
|
||||
icon: BookTextIcon,
|
||||
guidance_url: 'https://modrinth.com/legal/rules#miscellaneous',
|
||||
navigate: '/settings/license',
|
||||
actions: [
|
||||
{
|
||||
id: 'license_invalid_link',
|
||||
type: 'button',
|
||||
label: 'Invalid Link',
|
||||
weight: 600,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'medium',
|
||||
shouldShow: (project) => Boolean(project.license.url),
|
||||
message: async () => (await import('../messages/license/invalid_link.md?raw')).default,
|
||||
enablesActions: [
|
||||
{
|
||||
id: 'license_invalid_link-custom_license',
|
||||
type: 'toggle',
|
||||
label: 'Invalid Link: Custom License',
|
||||
weight: 601,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'medium',
|
||||
message: async () =>
|
||||
(await import('../messages/license/invalid_link-custom_license.md?raw')).default,
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// id: 'license_no_source',
|
||||
// type: 'conditional-button',
|
||||
// label: 'No Source',
|
||||
// fallbackWeight: 602,
|
||||
// suggestedStatus: 'rejected',
|
||||
// severity: 'medium',
|
||||
// fallbackMessage: async () => (await import('../messages/license/no_source.md?raw')).default,
|
||||
// messageVariants: [
|
||||
// {
|
||||
// conditions: {
|
||||
// requiredActions: ['reupload_unclear_fork'],
|
||||
// },
|
||||
// weight: 602,
|
||||
// message: async () => (await import('../messages/license/no_source-fork.md?raw')).default,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
id: 'license_no_source',
|
||||
type: 'button',
|
||||
label: 'No Source',
|
||||
weight: 602,
|
||||
suggestedStatus: 'rejected',
|
||||
severity: 'medium',
|
||||
shouldShow: (project) => !licensesNotRequiringSource.includes(project.license.id),
|
||||
message: async () => (await import('../messages/license/no_source.md?raw')).default,
|
||||
enablesActions: [
|
||||
{
|
||||
id: 'license_no_source-fork',
|
||||
type: 'toggle',
|
||||
label: 'No Source: Fork',
|
||||
weight: 602,
|
||||
suggestedStatus: 'rejected',
|
||||
severity: 'high',
|
||||
message: async () => (await import('../messages/license/no_source-fork.md?raw')).default,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default licenseStage
|
||||
@ -3,43 +3,81 @@ import type { ButtonAction } from '../../types/actions'
|
||||
import { LinkIcon } from '@modrinth/assets'
|
||||
|
||||
const links: Stage = {
|
||||
title: "Are the project's links accessible and not misleading?",
|
||||
title: "Are the project's links accurate and accessible?",
|
||||
id: 'links',
|
||||
icon: LinkIcon,
|
||||
guidance_url: 'https://modrinth.com/legal/rules#miscellaneous',
|
||||
guidance_url: 'https://modrinth.com/legal/rules',
|
||||
navigate: '/settings/links',
|
||||
shouldShow: (project) =>
|
||||
Boolean(
|
||||
project.issues_url ||
|
||||
project.source_url ||
|
||||
project.wiki_url ||
|
||||
project.discord_url ||
|
||||
project.donation_urls.length > 0,
|
||||
),
|
||||
text: async (project) => {
|
||||
let text = (await import('../messages/checklist-text/links/base.md?raw')).default
|
||||
|
||||
if (project.donation_urls.length > 0) {
|
||||
text += (await import('../messages/checklist-text/links/donation/donations.md?raw')).default
|
||||
|
||||
for (const donation of project.donation_urls) {
|
||||
text += (await import(`../messages/checklist-text/links/donation/donation.md?raw`)).default
|
||||
.replace('{URL}', donation.url)
|
||||
.replace('{PLATFORM}', donation.platform)
|
||||
}
|
||||
}
|
||||
|
||||
return text
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
id: 'links_misused',
|
||||
type: 'button',
|
||||
label: 'Links are misused',
|
||||
weight: 10,
|
||||
weight: 500,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'low',
|
||||
message: async () => (await import('../messages/links/misused.md?raw')).default,
|
||||
} as ButtonAction,
|
||||
{
|
||||
id: 'links_not_accessible_source',
|
||||
type: 'button',
|
||||
label: 'Not accessible (source)',
|
||||
weight: 10,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'low',
|
||||
message: async () => (await import('../messages/links/not-accessible-source.md?raw')).default,
|
||||
} as ButtonAction,
|
||||
{
|
||||
id: 'links_not_accessible_other',
|
||||
type: 'button',
|
||||
label: 'Not accessible (other)',
|
||||
weight: 10,
|
||||
suggestedStatus: 'flagged',
|
||||
severity: 'low',
|
||||
message: async () => (await import('../messages/links/not-accessible-other.md?raw')).default,
|
||||
relevantExtraInput: [
|
||||
{
|
||||
label: 'Please specify the link type that is inaccessible.',
|
||||
variable: 'LINK',
|
||||
required: true,
|
||||
label: 'What links are misused?',
|
||||
variable: 'MISUSED_LINKS',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
} as ButtonAction,
|
||||
{
|
||||
id: 'links_unaccessible',
|
||||
type: 'button',
|
||||
label: 'Links are inaccessible',
|
||||
weight: 510,
|
||||
suggestedStatus: 'flagged',
|
||||
// Theoretically a conditional could go here to prevent overlap of misuse and unaccessible messages repeating while still allowing for a multi-select in each.
|
||||
// if links_misused was selected, send nothing.
|
||||
message: async () => (await import('../messages/links/not_accessible.md?raw')).default,
|
||||
enablesActions: [
|
||||
{
|
||||
id: 'links_unaccessible_options',
|
||||
type: 'multi-select-chips',
|
||||
label: 'Warn of unaccessible link?',
|
||||
options: [
|
||||
{
|
||||
label: 'Source',
|
||||
weight: 511,
|
||||
shouldShow: (project) => Boolean(project.source_url),
|
||||
message: async () =>
|
||||
(await import('../messages/links/not_accessible-source.md?raw')).default,
|
||||
},
|
||||
{
|
||||
label: 'Discord',
|
||||
weight: 512,
|
||||
shouldShow: (project) => Boolean(project.discord_url),
|
||||
message: async () =>
|
||||
(await import('../messages/links/not_accessible-discord.md?raw')).default,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
} as ButtonAction,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user