Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
023c789
wip: delphi trace transform rules
aecsocket Jul 19, 2026
b6b19e5
add demo delphi rules ui
aecsocket Jul 22, 2026
3c77cc1
use Ace editor
aecsocket Jul 22, 2026
2815468
run rule scans
aecsocket Jul 22, 2026
ea42c64
show in/out schema
aecsocket Jul 22, 2026
4b07ed7
remove old ui
aecsocket Jul 23, 2026
8d82c22
rules affect tech review detail statuses
aecsocket Jul 24, 2026
f0dff8e
clean up __dummy stuff
aecsocket Jul 26, 2026
88c0915
Merge branch 'main' into boris/dev-1205-trace-rules
aecsocket Jul 26, 2026
36d3aab
fmt
aecsocket Jul 26, 2026
4c5e107
fix
aecsocket Jul 26, 2026
7c7efd5
Merge branch 'main' into boris/dev-1205-trace-rules
aecsocket Jul 27, 2026
18c95e2
prepr
aecsocket Jul 27, 2026
849943e
clean up db stuff
aecsocket Jul 27, 2026
b92fc10
cleanup
aecsocket Jul 27, 2026
cc77fd1
Merge branch 'main' into boris/dev-1205-trace-rules
aecsocket Jul 30, 2026
ddb45f6
move hidden to severity
aecsocket Aug 2, 2026
5ead149
flatten input context
aecsocket Aug 2, 2026
4c8abfb
adjust schema
aecsocket Aug 2, 2026
7f59fee
Merge branch 'main' of github.com:modrinth/code into boris/dev-1205-t…
aecsocket Aug 11, 2026
04d00c0
improve errors for CEL input
aecsocket Aug 12, 2026
26e1d64
live/outdated label on rules
aecsocket Aug 12, 2026
37d371e
Merge branch 'main' into boris/dev-1205-trace-rules
aecsocket Aug 13, 2026
7ef2492
prepare
aecsocket Aug 13, 2026
43818e4
add dummy fixture
aecsocket Aug 13, 2026
ac00e2b
allow returning strings from CEL as shorthand
aecsocket Aug 13, 2026
5cf9d42
fix up wrap err calls
aecsocket Aug 13, 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
82 changes: 79 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ bitflags = "2.9.4"
bon = "3.9.3"
bytemuck = "1.24.0"
bytes = "1.10.1"
cel = { version = "0.14.0", default-features = false, features = ["json"] }
censor = "0.3.0"
chardetng = "0.1.17"
chrono = "0.4.42"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@
<p class="m-0 break-words font-semibold text-contrast">
{{ trace.project_name }}
</p>
<p class="m-0 mt-1 flex flex-wrap items-center gap-1 text-sm text-secondary">
<span class="break-all">{{ trace.version_number }}</span>
<ChevronRightIcon class="size-4 shrink-0" aria-hidden="true" />
<span class="break-all">{{ decodeTracePath(trace.file_name) }}</span>
<template v-if="trace.jar">
<ChevronRightIcon class="size-4 shrink-0" aria-hidden="true" />
<span class="break-all">{{ decodeTracePath(trace.jar) }}</span>
</template>
<p class="m-0 mt-1 text-sm text-secondary">
<IssueDetailPath :segments="[trace.version_number, trace.file_name, trace.jar]" />
</p>
</div>
<div class="flex flex-wrap items-center gap-2">
Expand All @@ -31,9 +25,11 @@

<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import { ChevronRightIcon, ExternalIcon } from '@modrinth/assets'
import { ExternalIcon } from '@modrinth/assets'
import { Badge, ButtonLink } from '@modrinth/ui'

import IssueDetailPath from '~/components/ui/moderation/IssueDetailPath.vue'

const props = defineProps<{
trace: Labrinth.TechReview.Internal.GlobalIssueDetailTrace
}>()
Expand All @@ -44,12 +40,4 @@ const localTraceLink = computed(
props.trace.detail_id,
)}`,
)

function decodeTracePath(path: string): string {
try {
return decodeURIComponent(path)
} catch {
return path
}
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
</p>
<p class="m-0 break-all text-secondary">
<span class="font-semibold text-contrast">Path</span>
{{ decodeTracePath(getLatestLocalTrace(trace)?.file_path ?? '') }}
<IssueDetailPath :segments="[getLatestLocalTrace(trace)?.file_path]" />
</p>
</div>
</div>
Expand Down Expand Up @@ -131,6 +131,7 @@ import {
} from '@modrinth/ui'

import GlobalDetailLocalTraceCard from '~/components/ui/moderation/GlobalDetailLocalTraceCard.vue'
import IssueDetailPath from '~/components/ui/moderation/IssueDetailPath.vue'

const client = injectModrinthClient()
const { addNotification } = injectNotificationManager()
Expand Down Expand Up @@ -163,24 +164,19 @@ function getLatestLocalTrace(trace: Labrinth.TechReview.Internal.GlobalIssueDeta
return trace.local_traces.at(-1)
}

function decodeTracePath(path: string): string {
try {
return decodeURIComponent(path)
} catch {
return path
}
}

function getSeverityBadgeColor(
severity: Labrinth.TechReview.Internal.DelphiSeverity | undefined,
): string {
switch (severity) {
case 'malware':
case 'severe':
return 'border-red/60 bg-highlight-red text-red'
case 'high':
return 'border-orange/60 bg-highlight-orange text-orange'
case 'medium':
return 'border-green/60 bg-highlight-green text-green'
case 'hidden':
return 'border-divider bg-surface-2 text-secondary'
case 'low':
default:
return 'border-blue/60 bg-highlight-blue text-blue'
Expand Down
74 changes: 74 additions & 0 deletions apps/frontend/src/components/ui/moderation/IssueDetailPath.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<template>
<span class="inline-flex min-w-0 flex-wrap items-center gap-1">
<template v-for="(segment, index) in decodedSegments" :key="`${segment}-${index}`">
<ChevronRightIcon v-if="index > 0" class="size-4 shrink-0" aria-hidden="true" />
<span
v-tooltip="isTruncated(segment) ? segment : undefined"
class="break-all"
:class="{
'font-semibold text-contrast': emphasizeLast && index === decodedSegments.length - 1,
'text-secondary': emphasizeLast && index < decodedSegments.length - 1,
}"
>
{{ formatSegment(segment) }}
</span>
</template>
</span>
</template>

<script setup lang="ts">
import { ChevronRightIcon } from '@modrinth/assets'

const props = withDefaults(
defineProps<{
segments: readonly (string | null | undefined)[]
truncate?: boolean
maxLength?: number
emphasizeLast?: boolean
decode?: boolean
hideBaseMrpack?: boolean
}>(),
{
truncate: false,
maxLength: 120,
emphasizeLast: false,
decode: true,
hideBaseMrpack: false,
},
)

const decodedSegments = computed(() => {
const segments = props.segments
.flatMap((segment) => segment?.split('#') ?? [])
.filter((segment) => segment.length > 0)
.map((segment) => (props.decode ? decodePath(segment) : segment))

if (props.hideBaseMrpack && segments[0]?.toLowerCase().endsWith('.mrpack')) {
return segments.slice(1)
}

return segments
})

function decodePath(path: string): string {
try {
return decodeURIComponent(path)
} catch {
return path
}
}

function isTruncated(segment: string): boolean {
return props.truncate && segment.length > props.maxLength
}

function formatSegment(segment: string): string {
if (!isTruncated(segment)) return segment

const separator = '...'
const charsToShow = props.maxLength - separator.length
const frontChars = Math.ceil(charsToShow / 3)
const backChars = Math.floor((charsToShow * 2) / 3)
return segment.slice(0, frontChars) + separator + segment.slice(-backChars)
}
</script>
Loading
Loading