feat: smaller qa points

This commit is contained in:
Calum H. (IMB11)
2026-07-27 18:09:46 +01:00
parent 5b5468020d
commit f1f306d7ea
12 changed files with 109 additions and 38 deletions
+17 -18
View File
@@ -224,12 +224,11 @@ fn normalize_svg_from_path(icon_path: &Path) -> crate::Result<Bytes> {
))
})?;
if metadata.len() > INSTANCE_ICON_MAX_SVG_SOURCE_BYTES {
return Err(crate::ErrorKind::InputError(format!(
"SVG instance icons cannot exceed {} bytes before normalization",
INSTANCE_ICON_MAX_SVG_SOURCE_BYTES
))
.into());
}
return Err(crate::ErrorKind::InputError(format!(
"SVG instance icons cannot exceed {INSTANCE_ICON_MAX_SVG_SOURCE_BYTES} bytes before normalization"
))
.into());
}
let bytes = std::fs::read(icon_path).map_err(|error| {
crate::ErrorKind::InputError(format!(
@@ -245,15 +244,16 @@ fn normalize_svg(
resources_dir: Option<&Path>,
) -> crate::Result<Bytes> {
if bytes.len() as u64 > INSTANCE_ICON_MAX_SVG_SOURCE_BYTES {
return Err(crate::ErrorKind::InputError(format!(
"SVG instance icons cannot exceed {} bytes before normalization",
INSTANCE_ICON_MAX_SVG_SOURCE_BYTES
))
.into());
}
return Err(crate::ErrorKind::InputError(format!(
"SVG instance icons cannot exceed {INSTANCE_ICON_MAX_SVG_SOURCE_BYTES} bytes before normalization"
))
.into());
}
let mut options = resvg::usvg::Options::default();
options.resources_dir = resources_dir.map(Path::to_path_buf);
let mut options = resvg::usvg::Options {
resources_dir: resources_dir.map(Path::to_path_buf),
..Default::default()
};
options.fontdb_mut().load_system_fonts();
let tree =
resvg::usvg::Tree::from_data(bytes, &options).map_err(|error| {
@@ -308,9 +308,8 @@ fn has_svg_extension(path: &Path) -> bool {
}
fn icon_too_large_error() -> crate::Error {
crate::ErrorKind::InputError(format!(
"Instance icons cannot exceed {} bytes",
INSTANCE_ICON_MAX_BYTES
))
crate::ErrorKind::InputError(format!(
"Instance icons cannot exceed {INSTANCE_ICON_MAX_BYTES} bytes"
))
.into()
}
@@ -29,6 +29,7 @@ import type {
ContentCardProject,
ContentCardVersion,
ContentOwner,
ContentSource,
} from '../types'
const { formatMessage } = useVIntl()
@@ -46,6 +47,7 @@ interface Props {
version?: ContentCardVersion
versionLink?: string | RouteLocationRaw
owner?: ContentOwner
source?: ContentSource
enabled?: boolean
installing?: boolean
hasUpdate?: boolean
@@ -68,6 +70,7 @@ const props = withDefaults(defineProps<Props>(), {
version: undefined,
versionLink: undefined,
owner: undefined,
source: undefined,
enabled: undefined,
installing: false,
hasUpdate: false,
@@ -196,8 +199,32 @@ const deleteHovered = ref(false)
</div>
<div class="flex min-w-0 items-center gap-1">
<template v-if="source">
<AutoLink
:target="
typeof source.link === 'string' && source.link.startsWith('http')
? '_blank'
: undefined
"
:to="source.link"
class="flex min-w-0 items-center gap-1 !decoration-secondary"
:class="{ 'hover:underline': source.link }"
>
<Avatar
:src="source.project.icon_url"
:alt="source.project.title"
:tint-by="source.project.id"
size="1.25rem"
no-shadow
class="shrink-0 rounded-md"
/>
<span class="truncate text-sm leading-5 text-secondary">
{{ source.project.title }}
</span>
</AutoLink>
</template>
<AutoLink
v-if="owner"
v-else-if="owner"
:target="
typeof owner.link === 'string' && owner.link.startsWith('http')
? '_blank'
@@ -264,6 +264,7 @@ function handleSort(column: ContentCardTableSortColumn) {
:version="item.version"
:version-link="item.versionLink"
:owner="item.owner"
:source="item.source"
:enabled="item.enabled"
:installing="item.installing"
:has-update="item.hasUpdate"
@@ -327,6 +328,7 @@ function handleSort(column: ContentCardTableSortColumn) {
:version="item.version"
:version-link="item.versionLink"
:owner="item.owner"
:source="item.source"
:enabled="item.enabled"
:installing="item.installing"
:has-update="item.hasUpdate"
@@ -20,6 +20,7 @@ import type { Option as OverflowMenuOption } from '#ui/components/base/OverflowM
import StyledInput from '#ui/components/base/StyledInput.vue'
import NewModal from '#ui/components/modal/NewModal.vue'
import { defineMessages, useVIntl } from '#ui/composables/i18n'
import { injectPageContext } from '#ui/providers/page-context'
import {
commonMessages,
commonProjectTypeCategoryMessages,
@@ -28,11 +29,12 @@ import {
} from '#ui/utils/common-messages'
import { getClientWarningType, isClientOnlyEnvironment } from '../../composables/content-filtering'
import type { ContentCardTableItem, ContentItem } from '../../types'
import type { ContentCardProject, ContentCardTableItem, ContentItem } from '../../types'
import ContentCardTable from '../ContentCardTable.vue'
import ContentSelectionBar from '../ContentSelectionBar.vue'
const { formatMessage } = useVIntl()
const pageContext = injectPageContext(null)
interface Props {
header?: string
@@ -263,6 +265,12 @@ const tableItems = computed<ContentCardTableItem[]>(() =>
link: `https://modrinth.com/${item.owner.type}/${item.owner.id}`,
}
: undefined,
source: item.source
? {
...item.source,
link: item.source.link ?? sourceProjectLink(item.source.project),
}
: undefined,
...(props.enableToggle ? { enabled: item.enabled } : {}),
installing: item.installing === true,
toggleDisabled: props.actionDisabled,
@@ -290,7 +298,7 @@ const tableItems = computed<ContentCardTableItem[]>(() =>
})),
)
const externalItemIds = computed(
() => new Set(items.value.filter((item) => item.external).map((item) => item.id)),
() => new Set(items.value.filter((item) => item.external && !item.source).map((item) => item.id)),
)
const externalSlicerUrls = computed(() => {
const urls: Record<string, string> = {}
@@ -332,6 +340,12 @@ function itemDisplayName(item: ContentItem) {
return item.project?.title ?? item.file_name
}
function sourceProjectLink(project: ContentCardProject) {
const projectId = project.slug ?? project.id
const url = `https://modrinth.com/modpack/${encodeURIComponent(projectId)}`
return pageContext ? () => pageContext.openExternalUrl(url) : url
}
function handleEnabledChange(id: string, value: boolean) {
if (props.actionDisabled) return
const item = items.value.find((item) => item.id === id)
@@ -21,6 +21,11 @@ export interface ContentOwner {
link?: string | RouteLocationRaw | (() => void)
}
export interface ContentSource {
project: ContentCardProject
link?: string | RouteLocationRaw | (() => void)
}
export type ClientWarningType = 'retained' | 'depends' | 'environment'
export type ContentSourceKind =
@@ -44,6 +49,7 @@ export interface ContentCardTableItem {
version?: ContentCardVersion
versionLink?: string | RouteLocationRaw
owner?: ContentOwner
source?: ContentSource
enabled?: boolean
disabled?: boolean
disabledTooltip?: string | null