From cc5f303d211085b9e7987249cd3f4a94aa68eefa Mon Sep 17 00:00:00 2001 From: Cade Ayres Date: Tue, 10 Feb 2026 16:04:52 +0000 Subject: [PATCH 01/14] WIP: Added VulnerablePackageBanner and moved shared logic to composable. Improved background/text clarity # Conflicts: # quasar.config.ts # src/css/scheme/dark.scss # src/css/scheme/light.scss --- quasar.config.ts | 14 ++++++++- src/components/banner/ManagerUpdateBanner.vue | 14 ++++----- .../banner/VulnerablePackageBanner.vue | 13 +++++++++ .../VulnerablePackageComposable.ts | 29 +++++++++++++++++++ src/components/views/LocalModList.vue | 5 ++++ .../views/LocalModList/LocalModCard.vue | 19 +++++++++++- src/css/scheme/dark.scss | 3 ++ src/css/scheme/light.scss | 2 ++ src/css/v2/components/_card.scss | 14 +++++++++ 9 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 src/components/banner/VulnerablePackageBanner.vue create mode 100644 src/components/composables/VulnerablePackageComposable.ts diff --git a/quasar.config.ts b/quasar.config.ts index e9d7b21c7..e28148216 100644 --- a/quasar.config.ts +++ b/quasar.config.ts @@ -1,6 +1,7 @@ // Configuration for your app // https://v2.quasar.dev/quasar-cli-vite/quasar-config-file +import path from 'node:path'; import { defineConfig } from '#q-app/wrappers'; export default defineConfig((ctx) => { @@ -47,7 +48,13 @@ export default defineConfig((ctx) => { typescript: { strict: true, vueShim: true, - // extendTsConfig (tsConfig) {} + extendTsConfig(tsConfig) { + tsConfig.compilerOptions.paths = { + '@r2': ['../src'], + '@r2/*': ['../src/*'], + ...tsConfig.compilerOptions.paths, + }; + } }, vueRouterMode: 'history', // available values: 'hash', 'history' @@ -71,6 +78,11 @@ export default defineConfig((ctx) => { extendViteConf (viteConf) { // Force Vite to use esbuild for CSS, overriding any defaults viteConf.build!.cssMinify = 'esbuild'; + + // Allow @r2 alias + viteConf.resolve ??= {}; + viteConf.resolve.alias ??= {}; + (viteConf.resolve.alias as Record)['@r2'] = path.resolve(__dirname, 'src'); }, viteVuePluginOptions: { template: { diff --git a/src/components/banner/ManagerUpdateBanner.vue b/src/components/banner/ManagerUpdateBanner.vue index 93775ab23..5a1e64ccd 100644 --- a/src/components/banner/ManagerUpdateBanner.vue +++ b/src/components/banner/ManagerUpdateBanner.vue @@ -34,13 +34,11 @@ onMounted(async () => { diff --git a/src/components/banner/VulnerablePackageBanner.vue b/src/components/banner/VulnerablePackageBanner.vue new file mode 100644 index 000000000..53bb3a4b6 --- /dev/null +++ b/src/components/banner/VulnerablePackageBanner.vue @@ -0,0 +1,13 @@ + + + + + diff --git a/src/components/composables/VulnerablePackageComposable.ts b/src/components/composables/VulnerablePackageComposable.ts new file mode 100644 index 000000000..d6e993dcd --- /dev/null +++ b/src/components/composables/VulnerablePackageComposable.ts @@ -0,0 +1,29 @@ +/** + * Definitions are outside the composable function so that calculations are shared. + * We do not need to recalculate on a per-usage basis. + */ +import { getStore } from '@r2/providers/generic/store/StoreProvider'; +import { computed } from 'vue'; +import ManifestV2 from '@r2/model/ManifestV2'; +import ThunderstoreMod from '@r2/model/ThunderstoreMod'; + +const store = getStore(); + +const localModList = computed(() => store.state.profile.modList); +const onlineModList = computed>(() => { + const mods: ThunderstoreMod[] = store.state.tsMods.mods; + return new Map(mods.map(value => [value.getFullName(), value])); +}); + +const vulnerablePackages = computed(() => { + return localModList.value.filter(value => !value.isOnlineSource() && onlineModList.value.has(value.getName())); +}); + +const hasVulnerablePackages = computed(() => vulnerablePackages.value.length > 0); + +export function useVulnerablePackageComposable() { + return { + vulnerablePackages, + hasVulnerablePackages + } +} diff --git a/src/components/views/LocalModList.vue b/src/components/views/LocalModList.vue index 2da519543..3650a41a1 100644 --- a/src/components/views/LocalModList.vue +++ b/src/components/views/LocalModList.vue @@ -8,6 +8,7 @@ + @@ -37,9 +38,13 @@ import { State } from '../../store'; import { computed, defineAsyncComponent } from 'vue'; import SkeletonLocalModCard from './LocalModList/SkeletonLocalModCard.vue'; import ManagerUpdateBanner from '../banner/ManagerUpdateBanner.vue'; +import VulnerablePackageBanner from '@r2/components/banner/VulnerablePackageBanner.vue'; +import { useVulnerablePackageComposable } from '@r2/components/composables/VulnerablePackageComposable'; const store = getStore(); +const { hasVulnerablePackages } = useVulnerablePackageComposable(); + const LocalModDraggableList = defineAsyncComponent(() => import('./LocalModList/LocalModDraggableList.vue')); const visibleModList = computed(() => store.getters['profile/visibleModList']); diff --git a/src/components/views/LocalModList/LocalModCard.vue b/src/components/views/LocalModList/LocalModCard.vue index 33a6af0c1..5b4d0ddc4 100644 --- a/src/components/views/LocalModList/LocalModCard.vue +++ b/src/components/views/LocalModList/LocalModCard.vue @@ -12,6 +12,7 @@ import { splitToNameAndVersion } from '../../../utils/DependencyUtils'; import { computed, onMounted, ref, watch } from 'vue'; import { getStore } from '../../../providers/generic/store/StoreProvider'; import { State } from '../../../store'; +import { useVulnerablePackageComposable } from '@r2/components/composables/VulnerablePackageComposable'; const store = getStore(); @@ -21,6 +22,8 @@ type LocalModCardProps = { const props = defineProps(); +const { vulnerablePackages } = useVulnerablePackageComposable(); + const disabledDependencies = ref([]); const missingDependencies = ref([]); const disableChangePending = ref(false); @@ -33,6 +36,7 @@ const isDeprecated = computed(() => store.state.tsMods.deprecated.get(props.mod. const isLatestVersion = computed(() => store.getters['tsMods/isLatestVersion'](props.mod)); const localModList = computed(() => store.state.profile.modList); const tsMod = computed(() => store.getters['tsMods/tsMod'](props.mod)); +const isConcerningPackage = computed(() => vulnerablePackages.value.findIndex(value => value.getName() === props.mod.getName()) >= 0) async function updateDependencies() { if (props.mod.getDependencies().length === 0) { @@ -187,7 +191,9 @@ function dependencyStringToModName(x: string) { :enabled="mod.isEnabled()" :id="`${mod.getAuthorName()}-${mod.getName()}-${mod.getVersionNumber()}`" :image="mod.getIcon()" - :allowSorting="true"> + :allowSorting="true" + :class="[{'card--is-concern': isConcerningPackage}]" + >