Files
Rocketmc/packages/moderation
Truman Gao 9958600121 feat: managing project versions (#4811)
* start modal, working show modal

* add stages and implement MultiModalStage component

* add project versions context and add file button

* implement add files stage

* export interfaces

* move MultiStageModal to /base

* small update to file input

* add version types to api-client

* wrap version namespace under v3

* implement add details stage fields and loaders component

* start create MC versions stage

* implement changelog stage and bring width into a per stage concern

* implement loader picker with grouping

* improve grouping and sorting for loader picker

* use chips component

* small updaets

* fix loader icon color

* componentize mc version picker

* initial version of shift click to select range

* use newModal for markdown editor

* start add dependencies stage with search

* implement showing mod options in search

* componentize modselect and add version/dependency relation select

* hide version and dependency relation when no project selected

* fix project facet search

* implement api-client versions requests

* fix search api request facet type to be string

* fix new modal outer container scroll

* implement add dependency stage

* fix parse error

* add placeholders

* fix types

* update dependency row styles

* small change

* fix the types on manage versions to be correct with labrinth request bodies

* fix create version file parts

* use draft version ref in flow and implement proper file handlling

* use draft version ref for mc versions select

* implement reactive modal state and conditionally disabled next buttons

* ensure all data is using draftVersion ref

* remove shift click to select range since it sucks

* fix up add dependencies stage state/types

* small fixes

* implement adding dependencies connected to api calls and make adding dependencies work

* add final create version button config

* start create version backend call and bring versions table to project settings

* set add files stage width

* remove version file upload in project page

* small fix

* fix create version api call

* implement error handling

* implement mc versions search

* implement showing all mc versions

* small fix

* implement prefill data

* add success notification

* add cancel button

* add new dropzone file input

* run pnpm run fix

* add tailwind preset in ui package

* polish file version row

* fix modal widths

* hide added versions when no versions added

* implement add loaders stage

* implement small chips and small fixes

* implement grouping for all releases

* implement new all releases grouping

* implement better shift click for version select

* small fixes

* fix search input style

* delete versions provider and start project type inferring

* implement getting project type

* add versions empty state, add folder up icon and pnpm run fix

* implement create version in project versions table

* update side nav

* implement dynamic create version flow depending on project type and detected data

* add id to stages and fix calling setStage not working

* move added loaded out of loader picker

* remove selected and detected MC versions

* add loading message to dependency search and fix dependency type always being "required"

* fix components in ref

* fix width on dropdown

* implement toggle all mc versions based on state of last in range

* fix mc version text colour

* do proper clean up

* update loaders to use tag item

* update UI to use TagItem and better match styles

* handle detected data when setting primary file

* add progress bar

* hide progress bar for non-progress stage

* add loading state on submit

* properly cache dependencies projects/versions

* pnpm run fix

* add dragover show purple border on dropzone file input

* better handle added dependencies

* move versions in side nav

* implement adding file type

* fix api body format for file type

* implement working edit existing version
- working add/remove file
- working edit version details

* a step towards proper versions refresh

* add gallery to project settings

* actually figured out refresh versions

* move checklist into settings page

* remove editing version from version page and add button to versions table in project settings

* remove edit and delete buttons from gallery in project page

* add empty state messages for project page

* add default scroll bar styles

* implement support for new file types

* remove edit from dropdown in project page versions table

* redirect to settings page

* move changelog to row with actions

* fix overflow on added dependencies

* fix redirect

* update scroll styles

* implement add environment stage (create and modify version not persisting environment to backend)

* small style fixes

* small spacing fix

* small style fixes

* add a flag for loading dependency projects

* address PR comments

* fix modrinth ui imports

* use magic keys instead of window.addeventlistener

* add spacing in bottom of settings page

* useDebounceFn from vue

* fix inconsistent stroke

* persist scroll through

* fix remove button

* fix api fields

* fix version file dropdown: hide primary option in edit mode and fix setting initial value

* fix links in nags

* implement skipped field for skipping steps instead of mutating stages array's elements

* implement suggested dependencies components

* implement suggested dependencies api call

* refactor cached get project and get version calls

* always hide environments

* update links

* set scroll in 10ms

* update links

* fix links pt2

* fix shadow

* fix progress bar

* dont include mc versions in suggested versions finder

* fix text overflow styles

* use tooltip

* fix change version name api

* implement set environment api call

* delete unused vue pages

* implement detected environment, edit environment step, and fix showing loaders in details for no loader projects

* small fix

* no loaders project wrong check

* fix not having 'minecraft' loader for resource pack

* implement updating existing files file type

* move add minecraft loader outside try catch

* add datapack to have environment

* fix being able to select duplicate MC versions

* remove datapack project from environment

* fix version fetch

* fix having detected environment not properly skipping step

* only add detected data when primary file changes

* fix unknown environemtn

* implement gallery and versions have moved admonition

* update project page for creator view

* small copy update

* merge fixes

* pnpm run fix

* fix checkmark squished

* fix version type can be deselected

* refactor: DI context + better typed MultistageModal

* fix type import

* Misc QA fixes

* fix allowed file types with no project type

* implement new add files stage

* fix versiosn header with new pagination

* hide buttons when no files for add file stage

* use prettier formatter

* allow signature file types

* add detecting primary file

* fix progress bar in firefox

* fix environment not correctly being hidden/shown

* remove environment missing nag

* temp bring back environment page

* remove delete version action from project page

* replace "continue" next button label with actual next step

* fix types

* pnpm run fix

* move supplementary files alert up and update border radius style on dropzone

* copy updates

* small update on version num placeholder

* update placeholder

* make timeout on upload routes 2 minutes

* fix lint issues

* run pnpm intl:extract

---------

Co-authored-by: Calum H. (IMB11) <contact@cal.engineer>
2025-12-18 19:56:15 +00:00
..
2025-07-11 16:09:04 +00:00

@modrinth/moderation

This package contains both the moderation checklist system used by moderators for reviewing projects on Modrinth, and the publishing checklist (nag system) that provides automated feedback to project authors during the submission process.

Structure

The package is organized as follows:

/packages/moderation/
├── data/
│   ├── checklist.ts        # Main moderation checklist definition - imports and exports all stages
│   ├── messages/           # Markdown files containing message templates for moderation
│   │   ├── title/          # Messages for the title stage
│   │   ├── description/    # Messages for the description stage
│   │   └── ...             # One directory per stage
│   ├── stages/             # Moderation stage definition files
│   │   ├── title.ts        # Title stage definition
│   │   ├── description.ts  # Description stage definition
│   │   └── ...             # One file per stage
│   └── nags/               # Publishing checklist (nag system) files
│       ├── core.ts         # Core nags (required fields, basic validation)
│       └── ...
└── types/                  # Type definitions
    ├── actions.ts          # Action-related types (moderation)
    ├── messages.ts         # Message-related types (moderation)
    ├── stage.ts            # Stage-related types (moderation)
    └── nags.ts             # Nag-related types (publishing checklist)

Moderation Checklist System

The moderation checklist provides a structured and transparent way to define moderation stages, actions, and messages that are displayed to moderators during the review process.

Stages

A stage represents a discrete step in the moderation process, like checking a project's title, description, or links. Each stage has:

  • A title displayed to moderators
  • A link to guidance documentation
  • An optional navigation path to direct moderators to the relevant part of the project page
  • A list of actions that moderators can take

Stages are defined in individual files in the data/stages directory and are assembled into the complete checklist in data/checklist.ts.

Actions

Actions represent decisions moderators can make for each stage. They can be buttons, dropdowns, toggles, etc. Actions can have:

  • Labels displayed to the moderator
  • Messages that are included in the final moderation decision
  • Suggested moderation status and severity
  • Optional text inputs for additional information
  • Conditional behavior based on other selected actions

Each action requires a unique id field that is used for conditional logic and action relationships. The suggestedStatus and severity fields help determine the overall moderation outcome.

Messages

Messages are the actual text that will be included in communications to project authors. To promote maintainability and reuse, messages are stored as Markdown files in the data/messages directory, organized by stage.

Variable replacement

You can use variables in your messages that will be replaced with user input:

  1. Define a variable in the relevantExtraInput array of an action:
relevantExtraInput: [
  {
    label: 'Explanation for the user',
    variable: 'MESSAGE',
    required: true,
  },
],
  1. Use the variable in your message with %VARIABLE% syntax:
# Your Message Title

Here is some explanation about the issue.

%MESSAGE%

More text after the variable.

The %MESSAGE% placeholder will be replaced with the text entered by the moderator.

Conditional logic

The moderation system supports conditional behavior that changes based on the selection of other actions.

Conditional messages

You can define different messages for an action based on other selected actions:

{
  id: 'my_action',
  type: 'button',
  label: 'My Action',
  weight: 100,
  message: async () => (await import('../messages/default-message.md?raw')).default,
  conditionalMessages: [
    {
      conditions: {
        requiredActions: ['other_action_id'],
        excludedActions: ['another_action_id']
      },
      message: async () => (await import('../messages/conditional-message.md?raw')).default,
    }
  ]
}

Enabling and disabling actions

Actions can enable or disable other actions when selected:

{
  id: 'parent_action',
  type: 'button',
  label: 'Parent Action',
  // This will show these actions when parent_action is selected
  enablesActions: [
    {
      id: 'child_action',
      type: 'button',
      label: 'Child Action',
      // ...other properties
    }
  ],
  // This will hide actions with these IDs when parent_action is selected
  disablesActions: ['incompatible_action_id']
}

Conditional text inputs

Text inputs can be conditionally shown based on selected actions:

relevantExtraInput: [
	{
		label: 'Additional Information',
		variable: 'INFO',
		showWhen: {
			requiredActions: ['specific_action_id'],
			excludedActions: ['incompatible_action_id'],
		},
	},
]

Publishing Checklist (Nag System)

The nag system provides automated feedback to project authors during the submission process, helping them improve their projects before they reach moderation. It analyzes project data and provides suggestions, warnings, and requirements.

Nags

A nag represents a specific issue or suggestion for improvement. Each nag has:

  • A unique id for identification
  • A title and description displayed to the user
  • A status indicating severity: 'required', 'warning', or 'suggestion'
  • A shouldShow function that determines when the nag should be displayed
  • An optional link to help users address the issue

Internationalization

Use vintl's defineMessage syntax.

If you want to use context in the messages, you can do so like this:

description: (context: NagContext) => {
  const { formatMessage } = useVIntl()

  return formatMessage(defineMessage(...), {
    length: context.project.body?.length || 0,
    minChars: MIN_DESCRIPTION_CHARS,
  })
}

Nag Context

The NagContext type provides access to:

  • project: Current project data
  • versions: Project versions
  • tags: Frontend "tags" (generated state)
  • currentRoute: Current page route
  • and other data...

Adding New Nags

To add a new nag:

  1. Add the nag definition to the appropriate category file (or make a new category file and add it to data/nags.ts)
  2. Add corresponding i18n messages to the .i18n.ts file
  3. Implement the shouldShow logic based on project state
  4. Add appropriate links to help users resolve the issue
  5. Run pnpm run fix to fix lint issues & generate the root locale index.json file.

Example:

// In description.ts
{
  id: 'new-nag',
  title: messages.newNagTitle,
  description: messages.newNagDescription,
  status: 'warning',
  shouldShow: (context: NagContext) => {
    // Your validation logic here
    return someCondition
  },
  link: {
    path: 'settings/description',
    title: messages.editDescriptionTitle,
    shouldShow: (context: NagContext) => context.currentRoute !== 'type-id-settings-description',
  },
}
// In description.i18n.ts
newNagTitle: {
  id: 'nags.new-nag.title',
  defaultMessage: 'New Nag Title',
},
newNagDescription: {
  id: 'nags.new-nag.description',
  defaultMessage: 'Description of the new nag issue.',