Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
137 commits
Select commit Hold shift + click to select a range
540d58c
feat: start modview layout
tdgao Sep 15, 2026
b7602e7
feat: project info panel
tdgao Sep 16, 2026
49e3c8d
rename: workspace to layout
tdgao Sep 16, 2026
8d2238b
feat: add default layout configuration + double click divider to reset
tdgao Sep 16, 2026
bdf8338
feat: polish layout and styles/transitions
tdgao Sep 16, 2026
5764cbe
feat: scaffold out the main content panels
tdgao Sep 16, 2026
bad324e
refactor: clean up bg
tdgao Sep 17, 2026
f2af91d
feat: style project info panel
tdgao Sep 17, 2026
632892a
pnpm prepr
tdgao Sep 17, 2026
5ab4492
fix: missing m-0 for p tags
tdgao Sep 17, 2026
9c9e578
small style fix
tdgao Sep 17, 2026
7207447
small style fix on member
tdgao Sep 17, 2026
7e9acd0
again
tdgao Sep 17, 2026
05a5921
feat: popover panels for review stages
tdgao Sep 18, 2026
9e0f02c
feat: polish version cards
tdgao Sep 18, 2026
54f6a7d
polish
tdgao Sep 18, 2026
31ab726
feat: hook up rest of stages from existing checklist
tdgao Sep 18, 2026
01f28f7
feat: generate message when buttons are toggled
tdgao Sep 18, 2026
26ef881
feat: add new issues framework
tdgao Sep 21, 2026
548c61e
remove: side panel transition
tdgao Sep 21, 2026
1c17526
feat: add pin panel and polish conversation thread
tdgao Sep 21, 2026
7c9a269
fix: review panel hover race
tdgao Sep 21, 2026
c2cfe61
feat: add ai icon message
tdgao Sep 21, 2026
c5738b1
feat: implement disclosures for modview
tdgao Sep 21, 2026
739e737
feat: polish version cards content
tdgao Sep 22, 2026
6d26cc0
feat: polish panels and spacing
tdgao Sep 22, 2026
5be15d4
feat: polish panel and more styles
tdgao Sep 22, 2026
3e1a770
feat: polish layout, spacing, and sizing
tdgao Sep 22, 2026
d0c0000
feat: add modpack permissions to project review
tdgao Sep 22, 2026
79c1143
feat: add private note tab
tdgao Sep 22, 2026
4dedb8b
fix: review panel hides when selecting option in combobox
tdgao Sep 22, 2026
9e6a290
fix: missing external link button
tdgao Sep 22, 2026
a7583fe
feat: gap in main content
tdgao Sep 22, 2026
47023df
fix: review panel padding
tdgao Sep 22, 2026
ef6f2b6
fix: toggle transition all
tdgao Sep 22, 2026
73fc3d5
feat: add keyboard shortcuts in project review
tdgao Sep 22, 2026
d501b99
refactor: move review panel registry to moderation package owned
tdgao Sep 22, 2026
7826c9d
fix: wrong close panel arrow
tdgao Sep 22, 2026
49a98f1
fix: muted active tags
tdgao Sep 22, 2026
5e36119
feat: add issues list panel, issue picker, and issue cards
tdgao Sep 24, 2026
8606195
fix: qa around links and license
tdgao Sep 24, 2026
0271d8e
feat: add favicon for project review page
tdgao Sep 24, 2026
e7ee135
feat: go to next in queue after submitting review outcome
tdgao Sep 24, 2026
62552aa
feat: make panel.title optional and add page title
tdgao Sep 24, 2026
2d2f416
feat: bring back guidance Url
tdgao Sep 24, 2026
190b382
fix: dock layout is reset for conditional tabs
tdgao Sep 24, 2026
e48bab4
feat: add orgs to members section, add permissions number in tab, fix…
tdgao Sep 24, 2026
d03c2d8
feat: add server project details section and polish permissions
tdgao Sep 24, 2026
1fbd254
remove: gallery image specific review panels
tdgao Sep 24, 2026
e06ca3d
feat: editable fields for title, slug, summary, tags, links, license,…
tdgao Sep 25, 2026
bfbeae4
feat: style tabs as tabs
tdgao Sep 25, 2026
addb266
feat: add server details review panel
tdgao Sep 26, 2026
81b3701
feat: add review options to server details panel
coolbot100s Sep 26, 2026
bdbd894
feat: make contact input optional.
coolbot100s Sep 27, 2026
fa310fa
fix: ai disclosure section positioning
coolbot100s Sep 27, 2026
518cd2c
feat: allow any all or none inputs on reupload/reupload
coolbot100s Sep 27, 2026
b92fa93
fix: various disclosure options and empty list state
coolbot100s Sep 27, 2026
c847baa
fix: more consistent disclosure toggle labeling
coolbot100s Sep 27, 2026
02aa126
fix: mark optional fields as optional
coolbot100s Sep 27, 2026
b0ec9c1
allow moderating already approved projects
chyzman Sep 27, 2026
f67e0e2
fix: prepr
coolbot100s Sep 28, 2026
8e44cf5
Merge branch 'main' into truman/modview
tdgao Sep 28, 2026
b09de63
Merge branch 'chyz/moderate-approved-projects' into truman/modview
coolbot100s Sep 28, 2026
d36de28
Merge branch 'truman/modview' of https://github.com/modrinth/code int…
coolbot100s Sep 28, 2026
7d0364d
feat: more context for review panels
tdgao Sep 28, 2026
94fd479
Merge branch 'truman/modview' of github.com:modrinth/code into truman…
tdgao Sep 28, 2026
e55cb36
fix: non english disclosure only show when theres disclosures
tdgao Sep 28, 2026
5ddc1df
feat: show misused disclosure only when disclosure enabled
tdgao Sep 28, 2026
d8c5797
feat: add issue priority
tdgao Sep 28, 2026
b3f7837
feat: add Alerts priority for temp server issue
tdgao Sep 28, 2026
7cdd129
feat: add pixel art rendering option for image viewer
tdgao Sep 28, 2026
23a5e47
fix: panel in a closed tab stays showing briefly
tdgao Sep 28, 2026
333bfd1
feat: add red star for required field labels
tdgao Sep 28, 2026
76fa1f2
fix: versions review panel was not hidden when shouldShow is false
tdgao Sep 28, 2026
cbb7cb9
fix: cant scroll page when leaving project review
tdgao Sep 28, 2026
605d5cb
feat: add page loading bar when going to next project
tdgao Sep 28, 2026
1d222ca
feat: persist selected issues in local storage
tdgao Sep 28, 2026
4e2ecce
feat: sort deps by type then name and accodion on deps
tdgao Sep 28, 2026
2aa1c38
feat: add open in slicer button
tdgao Sep 28, 2026
6fe80ac
feat: permissions QA
tdgao Sep 28, 2026
a938f58
feat: open derivative content link
tdgao Sep 28, 2026
de0096c
feat: add resize vertical for disclosure text areas and fix toggle sp…
tdgao Sep 28, 2026
b21edb2
fix: dont show license for serv proj
tdgao Sep 28, 2026
191bd0b
feat: show thread first in tabs, add issue count
tdgao Sep 28, 2026
cc98870
Merge branch 'main' into truman/modview
tdgao Sep 28, 2026
fef0f13
feat: field hover bg and modview review panel styling
tdgao Sep 29, 2026
606ffae
refactor: put panel findings count badge slotted in with section
tdgao Sep 29, 2026
c6b903f
fix: button sizing
tdgao Sep 29, 2026
79998d2
fix: popover covered content and remove dead code
tdgao Sep 29, 2026
991ec8a
feat: fancier icons on stuff
tdgao Sep 29, 2026
8df6b8b
feat: bold headings
tdgao Sep 29, 2026
0f6fbd3
feat: show author for server modpack content and show org stats
tdgao Sep 29, 2026
2f64ad2
feat: add project status icon to title
tdgao Sep 29, 2026
84c52c4
feat: rearrange queue buttons
tdgao Sep 29, 2026
99572d8
feat: add shared panel activation context and pressing hotkey to open…
tdgao Sep 29, 2026
9da86c3
feat: show issue message is edited and improve issue picker
tdgao Sep 29, 2026
9a4ced1
Merge branch 'main' into truman/modview
tdgao Sep 29, 2026
c7efc42
fix: clicking button in inline panel makes panel active state stick
tdgao Sep 29, 2026
bd690ca
fix: contrast is too high on toggle buttons in off state
tdgao Sep 29, 2026
8cec547
fix: anchored panel still sticky after toggling in it
tdgao Sep 29, 2026
a3667e4
small change
tdgao Sep 29, 2026
9bf25d3
remove: history tab
tdgao Sep 29, 2026
c96ca82
revert: conversation thread to match main
tdgao Sep 29, 2026
756e431
feat: hooking up moderation issues backend
tdgao Oct 2, 2026
5426198
Merge branch 'main' into truman/modview
tdgao Oct 2, 2026
d191fae
fix: license merge
tdgao Oct 2, 2026
383ffa2
pnpm prepr
tdgao Oct 2, 2026
46af7e3
small
tdgao Oct 2, 2026
3147549
feat: use review panel title for project review disclosure cards
tdgao Oct 2, 2026
31b44e1
feat: add failed tech review badge
tdgao Oct 2, 2026
592c26c
fix: show issue list in order of defined priority
tdgao Oct 2, 2026
31ed63a
fix(messaging): issues above instead of below
tdgao Oct 2, 2026
4dfd03e
feat: remove corrections applied and use suggestions/remove issue facets
tdgao Oct 3, 2026
37d6dbb
fix: types
tdgao Oct 3, 2026
3e2e5d5
refactor: rename to message-box
tdgao Oct 3, 2026
470bf91
feat: add resizable message box
tdgao Oct 3, 2026
9e67300
feat: add message library
tdgao Oct 3, 2026
686f091
feat: allow facets to use context
tdgao Oct 3, 2026
9a80ad0
feat: all issues are sent as issues, and with facet acknowledge check…
tdgao Oct 3, 2026
7e8374d
feat: add specific gallery image issues, update messaging where appro…
tdgao Oct 5, 2026
8465ad6
organize metadata and versions issues
tdgao Oct 5, 2026
3b63057
feat: add bulk apply environment suggestion
tdgao Oct 5, 2026
16d6166
fix: handle re-rev on links that have issues but were moved
tdgao Oct 5, 2026
d90031e
feat: add location option on issues
tdgao Oct 5, 2026
da7c058
feat: improve version card
tdgao Oct 6, 2026
07180bf
feat: browser history forward back works for queue
tdgao Oct 6, 2026
b4f86b4
Merge branch 'main' into truman/modview
tdgao Oct 6, 2026
d9986ed
pnpm prepr
tdgao Oct 6, 2026
5d71e3c
feat: license qa
tdgao Oct 6, 2026
ae0e927
feat: show member and org status in project page for staff
tdgao Oct 6, 2026
a72746e
feat: add option to disable auto go next on submit review
tdgao Oct 6, 2026
5a959f3
small
tdgao Oct 6, 2026
5aad7d1
feat: add persisted show pixel art images, and thread image viewer
tdgao Oct 6, 2026
d2109ec
fix: thread message not sent when approve project
tdgao Oct 6, 2026
66c8a32
feat: add thread image viewing to moderation thread too
tdgao Oct 6, 2026
9ed4416
feat: add monetization and visibility to editing options
tdgao Oct 6, 2026
17b0c41
feat: show org members better
tdgao Oct 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/frontend/COPYING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ This includes, but may not be limited to, the following files:
- src/public/favicon-light-32x32.png
- src/public/favicon-settings-32x32.png
- src/public/favicon-light-settings-32x32.png
- src/public/favicon-review.svg
- src/public/favicon-light-review.svg
- src/public/dev-favicons/\*
- src/public/news/\*

Expand Down
5 changes: 5 additions & 0 deletions apps/frontend/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { fileURLToPath } from 'url'
import svgLoader from 'vite-svg-loader'

import { GenericModrinthClient, type Labrinth } from '../../packages/api-client/src/index.ts'
import moderationMessages, {
moderationMessagesDirectory,
} from '../../packages/moderation/vite-plugin'

const STAGING_API_URL = 'https://staging-api.modrinth.com/v2/'
const STAGING_SHARED_INSTANCES_API_URL = 'https://staging-shared-instances.modrinth.com'
Expand All @@ -26,6 +29,7 @@ export default defineNuxtConfig({
srcDir: 'src/',
alias: {
'@modrinth/api-client': API_CLIENT_SOURCE,
'#messages': moderationMessagesDirectory,
},
app: {
head: {
Expand Down Expand Up @@ -81,6 +85,7 @@ export default defineNuxtConfig({
dedupe: ['vue'],
},
plugins: [
moderationMessages(),
svgLoader({
svgoConfig: {
plugins: [
Expand Down
4 changes: 4 additions & 0 deletions apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
"wrangler": "^4.115.0"
},
"dependencies": {
"@codemirror/commands": "^6.3.2",
"@codemirror/language": "^6.9.3",
"@codemirror/view": "^6.22.1",
"@contentauth/c2pa-web": "^0.13.1",
"@formatjs/intl-localematcher": "^0.5.4",
"@ltd/j-toml": "^1.38.0",
Expand All @@ -59,6 +62,7 @@
"ansi-to-html": "^0.7.2",
"chart.js": "^4.5.1",
"dayjs": "^1.11.7",
"dockview-vue": "^8.2.0",
"dompurify": "^3.1.7",
"@floating-ui/vue": "^2.0.1",
"fuse.js": "^6.6.2",
Expand Down
117 changes: 117 additions & 0 deletions apps/frontend/src/components/ProjectStatusStats.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<template>
<div class="mt-1 flex flex-wrap gap-x-2 gap-y-1 text-xs">
<Tooltip
v-for="stat in statusStats"
:key="stat.status"
:text="formatMessage(stat.message, { count: stat.count })"
:aria-label="formatMessage(stat.message, { count: stat.count })"
class="flex items-center gap-0.5 font-medium tabular-nums"
:class="stat.color"
>
<component :is="stat.icon" class="size-[12px] shrink-0" aria-hidden="true" />
<span aria-hidden="true">{{ stat.count }}</span>
</Tooltip>
</div>
</template>

<script setup lang="ts">
import { CheckIcon, ShieldAlertIcon } from '@modrinth/assets'
import { defineMessages, PROJECT_STATUS_ICONS, Tooltip, useVIntl } from '@modrinth/ui'
import { computed } from 'vue'

const props = defineProps<{
stats: { status: string; count: number }[]
}>()
const { formatMessage } = useVIntl()
const messages = defineMessages({
approvedCount: {
id: 'moderation.project-review.approvedCount',
defaultMessage: '{count} approved',
},
archivedCount: {
id: 'moderation.project-review.archivedCount',
defaultMessage: '{count} archived',
},
unlistedCount: {
id: 'moderation.project-review.unlistedCount',
defaultMessage: '{count} unlisted',
},
withheldCount: {
id: 'moderation.project-review.withheldCount',
defaultMessage: '{count} withheld',
},
processingCount: {
id: 'moderation.project-review.processingCount',
defaultMessage: '{count} under review',
},
draftCount: {
id: 'moderation.project-review.draftCount',
defaultMessage: '{count} draft',
},
rejectedCount: {
id: 'moderation.project-review.rejectedCount',
defaultMessage: '{count} rejected',
},
techReviewFailedCount: {
id: 'moderation.project-review.techReviewFailedCount',
defaultMessage: '{count} failed technical review',
},
privateCount: {
id: 'moderation.project-review.privateCount',
defaultMessage: '{count} private',
},
scheduledCount: {
id: 'moderation.project-review.scheduledCount',
defaultMessage: '{count} scheduled',
},
unknownCount: {
id: 'moderation.project-review.unknownCount',
defaultMessage: '{count} unknown',
},
})
const statusMessages = {
approved: messages.approvedCount,
archived: messages.archivedCount,
unlisted: messages.unlistedCount,
withheld: messages.withheldCount,
processing: messages.processingCount,
draft: messages.draftCount,
rejected: messages.rejectedCount,
private: messages.privateCount,
scheduled: messages.scheduledCount,
unknown: messages.unknownCount,
tech_review_failed: messages.techReviewFailedCount,
}
const statusColors = {
approved: 'text-green',
archived: 'text-purple',
unlisted: 'text-purple',
withheld: 'text-red',
processing: 'text-orange',
draft: 'text-blue',
rejected: 'text-red',
private: 'text-purple',
scheduled: 'text-orange',
unknown: 'text-orange',
tech_review_failed: 'text-red',
}
const statusStats = computed(() =>
Object.entries(statusMessages)
.map(([key, message]) => {
const status = key as keyof typeof statusMessages
return {
status,
message,
count: props.stats.find((stat) => stat.status === status)?.count ?? 0,
icon:
status === 'tech_review_failed'
? ShieldAlertIcon
: status === 'approved'
? CheckIcon
: PROJECT_STATUS_ICONS[status],
color: statusColors[status],
}
})
.filter((stat) => stat.count > 0),
)
</script>
102 changes: 102 additions & 0 deletions apps/frontend/src/components/project-review/description/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<template>
<div
ref="interactionScope"
class="editable-review-section flex h-full min-h-0 flex-col gap-2.5 overflow-y-auto overflow-x-hidden px-2 pb-2"
>
<div
class="sticky top-0 z-10 flex shrink-0 items-start justify-between gap-4 bg-surface-1 pt-2"
>
<ReviewPanel
mode="inline"
:target="{ kind: 'description' }"
hide-title
:disabled="isLoading || !!error"
:interaction-scope="interactionScope"
class="!w-auto min-w-0 flex-1 pb-2.5"
/>
<div class="mt-0.5 flex shrink-0 justify-end gap-1">
<template v-if="editing">
<Button type="quiet" size="xs" :disabled="saving" @click="resetEditing">
{{ formatMessage(commonMessages.cancelButton) }}
</Button>
<Button
type="colored"
color="brand"
size="xs"
:loading="saving"
:disabled="!valid"
@click="save"
>
{{ formatMessage(commonMessages.saveButton) }}
</Button>
</template>
<EditButton
v-else-if="project && !isLoading && !error"
:section="formatMessage(messages.description)"
:disabled="saving"
@click="startEditing"
/>
</div>
</div>
<div class="min-h-0 flex-1">
<p v-if="!selection" class="m-0 text-secondary">
{{ formatMessage(messages.empty) }}
</p>
<p v-else-if="isLoading" role="status" class="m-0">
{{ formatMessage(messages.loading) }}
</p>
<div v-else-if="error" role="alert">
<p class="m-0">{{ formatMessage(messages.loadError) }}</p>
<Button @click="refresh">{{ formatMessage(messages.retry) }}</Button>
</div>
<div v-else-if="editing" class="flex min-h-full flex-col gap-2">
<MarkdownEditor ref="editor" v-model="draft" :disabled="saving" />
<p v-if="validationMessage" class="m-0 text-sm text-red" role="alert">
{{ validationMessage }}
</p>
</div>
<ProjectPageDescription
v-else-if="project?.description?.trim()"
class="text-sm leading-tight"
:description="project.description"
/>
<p v-else class="m-0 text-secondary">
{{ formatMessage(messages.emptyDescription) }}
</p>
</div>
</div>
</template>

<script setup lang="ts">
import {
Button,
commonMessages,
MarkdownEditor,
ProjectPageDescription,
useVIntl,
} from '@modrinth/ui'
import { useTemplateRef } from 'vue'

import { injectProjectReviewPageContext } from '~/providers/project-review'

import { projectReviewMessages as messages } from '../messages'
import EditButton from '../project-info/edit/button.vue'
import { useProjectTextEdit } from '../project-info/edit/use-project-text-edit'
import ReviewPanel from '../review-panel/index.vue'

const { formatMessage } = useVIntl()
const interactionScope = useTemplateRef<HTMLElement>('interactionScope')
const { selection, isLoading, error, refresh } = injectProjectReviewPageContext()
const editor = useTemplateRef<InstanceType<typeof MarkdownEditor>>('editor')
const {
project,
saving,
editing,
draft,
valid,
validationMessage,
startEditing,
resetEditing,
save,
} = useProjectTextEdit('description', () => editor.value?.focus())
</script>
70 changes: 70 additions & 0 deletions apps/frontend/src/components/project-review/disclosures/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<template>
<div v-if="project" class="flex h-full min-h-0 flex-col gap-1 overflow-hidden">
<div class="shrink-0 p-2">
<ReviewPanel mode="inline" :target="{ kind: 'disclosures' }" hide-title />
</div>
<p v-if="isPending" role="status">{{ formatMessage(statusMessages.loading) }}</p>
<div v-else-if="isError" role="alert">
<p>{{ formatMessage(statusMessages.loadError) }}</p>
<Button @click="refetch()">{{ formatMessage(statusMessages.retry) }}</Button>
</div>
<EmptyState
v-else-if="!canEditDisclosures"
type="no-documents"
:heading="formatMessage(editorMessages.uploadVersionFirstHeading)"
:description="formatMessage(commonMessages.uploadVersionsEmptyStateDescription)"
/>
<template v-else>
<div class="min-h-0 flex-1 overflow-y-auto overscroll-contain px-2 pb-2 pt-3">
<DisclosureCards
:editor="disclosures"
:project-title="project.name"
variant="review"
hide-description
/>
</div>
<div class="z-10 mt-auto shrink-0">
<p v-if="saveError" role="alert" class="text-red">
{{ formatMessage(statusMessages.saveError) }}
</p>
<UnsavedChangesPopup
inline
:original="savedSnapshot"
:modified="currentSnapshot"
:saving="saving"
:can-save="canSave"
:save-disabled-reason="saveDisabledReason"
@reset="reset"
@save="save"
/>
</div>
</template>
</div>
</template>

<script setup lang="ts">
import { Button, commonMessages, EmptyState, UnsavedChangesPopup, useVIntl } from '@modrinth/ui'

import DisclosureCards from '~/components/ui/project-settings/disclosures/DisclosureCards.vue'
import { disclosureStatusMessages as statusMessages } from '~/components/ui/project-settings/disclosures/messages'
import { injectProjectReviewPageContext } from '~/providers/project-review'

import ReviewPanel from '../review-panel/index.vue'

const { formatMessage } = useVIntl()
const { project, disclosures } = injectProjectReviewPageContext()
const {
messages: editorMessages,
savedSnapshot,
currentSnapshot,
saving,
reset,
save,
canSave,
saveDisabledReason,
canEditDisclosures,
disclosuresQuery,
saveError,
} = disclosures
const { isPending, isError, refetch } = disclosuresQuery
</script>
Loading
Loading