Merge remote-tracking branch 'upstream/main' into beta

This commit is contained in:
2025-08-16 22:38:14 +03:00
844 changed files with 105393 additions and 102744 deletions
+2 -3
View File
@@ -3,8 +3,7 @@ root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
indent_style = "tab"
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
@@ -15,5 +14,5 @@ indent_size = 2
max_line_length = off
trim_trailing_whitespace = false
[*.{json,yml,yaml,ts,vue,scss,css,html,js,cjs,mjs,gltf,prettierrc}]
[*.{json,yml,yaml}]
indent_size = 2
+9 -3
View File
@@ -2,8 +2,14 @@
"prettier.endOfLine": "lf",
"editor.formatOnSave": true,
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
"editor.detectIndentation": true,
"editor.detectIndentation": false,
"editor.insertSpaces": false,
"files.eol": "\n",
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
"source.fixAll.eslint": "explicit",
"source.organizeImports": "always"
},
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
+1 -1
View File
@@ -2,7 +2,7 @@
All packages in this repository are licensed under their respective licenses. For more information, refer to the LICENSE file in each package.
For detailed information, consult each package's COPYING.md file, if available.
For detailed information, consult each package's COPYING.md, LICENSE.txt, or LICENSE file, if available.
## Modrinth Branding
Generated
+306 -272
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1,2 +1,3 @@
**/dist
*.gltf
src/locales/
+2 -22
View File
@@ -1,22 +1,2 @@
import { createConfigForNuxt } from '@nuxt/eslint-config/flat'
import { fixupPluginRules } from '@eslint/compat'
import turboPlugin from 'eslint-plugin-turbo'
export default createConfigForNuxt().append([
{
name: 'turbo',
plugins: {
turbo: fixupPluginRules(turboPlugin),
},
rules: {
'turbo/no-undeclared-env-vars': 'error',
},
},
{
name: 'modrinth',
rules: {
'vue/html-self-closing': 'off',
'vue/multi-word-component-names': 'off',
},
},
])
import config from '@modrinth/tooling-config/eslint/nuxt.mjs'
export default config
+1
View File
@@ -11,6 +11,7 @@
<body>
<div id="app"></div>
<script src="https://tally.so/widgets/embed.js" async></script>
<script type="module" src="/src/main.js"></script>
</body>
</html>
+1 -2
View File
@@ -41,6 +41,7 @@
"vue-virtual-scroller": "v2.0.0-beta.8"
},
"devDependencies": {
"@modrinth/tooling-config": "workspace:*",
"@eslint/compat": "^1.1.1",
"@formatjs/cli": "^6.2.12",
"@nuxt/eslint-config": "^0.5.6",
@@ -48,13 +49,11 @@
"@vitejs/plugin-vue": "^5.0.4",
"autoprefixer": "^10.4.19",
"eslint": "^9.9.1",
"eslint-config-custom": "workspace:*",
"eslint-plugin-turbo": "^2.5.4",
"postcss": "^8.4.39",
"prettier": "^3.2.5",
"sass": "^1.74.1",
"tailwindcss": "^3.4.4",
"tsconfig": "workspace:*",
"typescript": "^5.5.4",
"vite": "^5.4.6",
"vue-tsc": "^2.1.6"
+196 -38
View File
@@ -1,6 +1,4 @@
<script setup>
import { computed, onMounted, onUnmounted, ref, watch, provide } from 'vue'
import { RouterView, useRoute, useRouter } from 'vue-router'
import {
ArrowBigUpDashIcon,
ChangeSkinIcon,
@@ -13,21 +11,23 @@ import {
LogOutIcon,
MaximizeIcon,
MinimizeIcon,
NewspaperIcon,
NotepadTextIcon,
PlusIcon,
RestoreIcon,
RightArrowIcon,
SettingsIcon,
WorldIcon,
XIcon,
NewspaperIcon,
} from '@modrinth/assets'
import {
Avatar,
Button,
ButtonStyled,
Notifications,
OverflowMenu,
NewsArticleCard,
NotificationPanel,
OverflowMenu,
provideNotificationManager,
} from '@modrinth/ui'
import { useLoading, useTheming } from '@/store/state'
// import ModrinthAppLogo from '@/assets/modrinth_app.svg?component'
@@ -39,46 +39,54 @@ import RunningAppBar from '@/components/ui/RunningAppBar.vue'
import SplashScreen from '@/components/ui/SplashScreen.vue'
import ErrorModal from '@/components/ui/ErrorModal.vue'
import ModrinthLoadingIndicator from '@/components/LoadingIndicatorBar.vue'
import { handleError, useNotifications } from '@/store/notifications.js'
import { command_listener, warning_listener } from '@/helpers/events.js'
import { type } from '@tauri-apps/plugin-os'
import { getOS, isDev } from '@/helpers/utils.js'
import { debugAnalytics, initAnalytics, optOutAnalytics, trackEvent } from '@/helpers/analytics'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { renderString } from '@modrinth/utils'
import { getVersion } from '@tauri-apps/api/app'
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
import { create_profile_and_install_from_file } from './helpers/pack'
import { useError } from '@/store/error.js'
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
import { invoke } from '@tauri-apps/api/core'
import { openUrl } from '@tauri-apps/plugin-opener'
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
import { $fetch } from 'ofetch'
import { computed, onMounted, onUnmounted, provide, ref, watch } from 'vue'
import { RouterView, useRoute, useRouter } from 'vue-router'
import FriendsList from '@/components/ui/friends/FriendsList.vue'
import IncompatibilityWarningModal from '@/components/ui/install_flow/IncompatibilityWarningModal.vue'
import InstallConfirmModal from '@/components/ui/install_flow/InstallConfirmModal.vue'
import { useInstall } from '@/store/install.js'
import { invoke } from '@tauri-apps/api/core'
import { get_opening_command, initialize_state } from '@/helpers/state'
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
import { renderString } from '@modrinth/utils'
import { useFetch } from '@/helpers/fetch.js'
// import { check } from '@tauri-apps/plugin-updater'
import NavButton from '@/components/ui/NavButton.vue'
import { cancelLogin, get as getCreds, login, logout } from '@/helpers/mr_auth.js'
import { get_user } from '@/helpers/cache.js'
import AppSettingsModal from '@/components/ui/modal/AppSettingsModal.vue'
import AuthGrantFlowWaitModal from '@/components/ui/modal/AuthGrantFlowWaitModal.vue'
// import PromotionWrapper from '@/components/ui/PromotionWrapper.vue'
// import { hide_ads_window, init_ads_window } from '@/helpers/ads.js'
import FriendsList from '@/components/ui/friends/FriendsList.vue'
import { openUrl } from '@tauri-apps/plugin-opener'
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
import QuickInstanceSwitcher from '@/components/ui/QuickInstanceSwitcher.vue'
import { get_available_capes, get_available_skins } from './helpers/skins'
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
import { list } from '@/helpers/profile.js'
import { getOS, isDev } from '@/helpers/utils.js'
import { useError } from '@/store/error.js'
import { create_profile_and_install_from_file } from './helpers/pack'
import { generateSkinPreviews } from './helpers/rendering/batch-skin-renderer'
import { get_available_capes, get_available_skins } from './helpers/skins'
import { AppNotificationManager } from './providers/app-notifications'
// [AR] Feature
import { getRemote, updateState } from '@/helpers/update.js'
const themeStore = useTheming()
const notificationManager = new AppNotificationManager()
provideNotificationManager(notificationManager)
const { handleError, addNotification } = notificationManager
const news = ref([])
const availableSurvey = ref(false)
const urlModal = ref(null)
@@ -115,15 +123,14 @@ onUnmounted(() => {
})
async function setupApp() {
stateInitialized.value = true
const settings = await get()
// Patched
// [AR] Patched
settings.personalized_ads = false
settings.telemetry = false
await set(settings)
stateInitialized.value = true
const {
native_decorations,
theme,
@@ -136,8 +143,7 @@ async function setupApp() {
toggle_sidebar,
developer_mode,
feature_flags,
} = settings
} = await get()
if (default_page === 'Library') {
await router.push('/library')
@@ -164,7 +170,7 @@ async function setupApp() {
isMaximized.value = await getCurrentWindow().isMaximized()
})
initAnalytics()
// initAnalytics()
if (!telemetry) {
console.info("[AR] • Telemetry disabled by default (Hard patched).")
optOutAnalytics()
@@ -186,7 +192,7 @@ async function setupApp() {
}
await warning_listener((e) =>
notificationsWrapper.value.addNotification({
addNotification({
title: 'Warning',
text: e.message,
type: 'warn',
@@ -240,6 +246,12 @@ async function setupApp() {
} catch (error) {
console.warn('Failed to generate skin previews in app setup.', error)
}
if (osType === 'windows') {
await processPendingSurveys()
} else {
console.info('Skipping user surveys on non-Windows platforms')
}
}
const stateFailed = ref(false)
@@ -271,9 +283,6 @@ const route = useRoute()
const loading = useLoading()
loading.setEnabled(false)
const notifications = useNotifications()
const notificationsWrapper = ref()
const error = useError()
const errorModal = ref()
@@ -355,8 +364,6 @@ const sidebarVisible = computed(() => sidebarToggled.value || forceSidebar.value
onMounted(() => {
invoke('show_window')
notifications.setNotifs(notificationsWrapper.value)
error.setErrorModal(errorModal.value)
install.setIncompatibilityWarningModal(incompatibilityWarningModal)
@@ -432,6 +439,116 @@ function handleAuxClick(e) {
e.target.dispatchEvent(event)
}
}
function cleanupOldSurveyDisplayData() {
const threeWeeksAgo = new Date()
threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21)
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i)
if (key.startsWith('survey-') && key.endsWith('-display')) {
const dateValue = new Date(localStorage.getItem(key))
if (dateValue < threeWeeksAgo) {
localStorage.removeItem(key)
}
}
}
}
async function openSurvey() {
if (!availableSurvey.value) {
console.error('No survey to open')
return
}
const creds = await getCreds().catch(handleError)
const userId = creds?.user_id
const formId = availableSurvey.value.tally_id
const popupOptions = {
layout: 'modal',
width: 700,
autoClose: 2000,
hideTitle: true,
hiddenFields: {
user_id: userId,
},
onOpen: () => console.info('Opened user survey'),
onClose: () => {
console.info('Closed user survey')
// show_ads_window()
},
onSubmit: () => console.info('Active user survey submitted'),
}
try {
// hide_ads_window()
if (window.Tally?.openPopup) {
console.info(`Opening Tally popup for user survey (form ID: ${formId})`)
dismissSurvey()
window.Tally.openPopup(formId, popupOptions)
} else {
console.warn('Tally script not yet loaded')
// show_ads_window()
}
} catch (e) {
console.error('Error opening Tally popup:', e)
// show_ads_window()
}
console.info(`Found user survey to show with tally_id: ${formId}`)
window.Tally.openPopup(formId, popupOptions)
}
function dismissSurvey() {
localStorage.setItem(`survey-${availableSurvey.value.id}-display`, new Date())
availableSurvey.value = undefined
}
async function processPendingSurveys() {
function isWithinLastTwoWeeks(date) {
const twoWeeksAgo = new Date()
twoWeeksAgo.setDate(twoWeeksAgo.getDate() - 14)
return date >= twoWeeksAgo
}
cleanupOldSurveyDisplayData()
const creds = await getCreds().catch(handleError)
const userId = creds?.user_id
const instances = await list().catch(handleError)
const isActivePlayer =
instances.findIndex(
(instance) =>
isWithinLastTwoWeeks(instance.last_played) && !isWithinLastTwoWeeks(instance.created),
) >= 0
let surveys = []
try {
surveys = await $fetch('https://api.modrinth.com/v2/surveys')
} catch (e) {
console.error('Error fetching surveys:', e)
}
const surveyToShow = surveys.find(
(survey) =>
!!(
localStorage.getItem(`survey-${survey.id}-display`) === null &&
survey.type === 'tally_app' &&
((survey.condition === 'active_player' && isActivePlayer) ||
(survey.assigned_users?.includes(userId) && !survey.dismissed_users?.includes(userId)))
),
)
if (surveyToShow) {
availableSurvey.value = surveyToShow
} else {
console.info('No user survey to show')
}
}
</script>
<template>
@@ -491,7 +608,6 @@ function handleAuxClick(e) {
<PlusIcon />
</NavButton>
<div class="flex flex-grow"></div>
<!-- [AR] TODO -->
<!-- <NavButton v-if="updateAvailable" v-tooltip.right="'Install update'" :to="() => restartApp()">
<DownloadIcon />
</NavButton> -->
@@ -593,6 +709,28 @@ function handleAuxClick(e) {
:class="{ 'sidebar-enabled': sidebarVisible }"
>
<div class="app-viewport flex-grow router-view">
<transition name="popup-survey">
<div
v-if="availableSurvey"
class="w-[400px] z-20 fixed -bottom-12 pb-16 right-[--right-bar-width] mr-4 rounded-t-2xl card-shadow bg-bg-raised border-divider border-[1px] border-solid border-b-0 p-4"
>
<h2 class="text-lg font-extrabold mt-0 mb-2">Hey there Modrinth user!</h2>
<p class="m-0 leading-tight">
Would you mind answering a few questions about your experience with Modrinth App?
</p>
<p class="mt-3 mb-4 leading-tight">
This feedback will go directly to the Modrinth team and help guide future updates!
</p>
<div class="flex gap-2">
<ButtonStyled color="brand">
<button @click="openSurvey"><NotepadTextIcon /> Take survey</button>
</ButtonStyled>
<ButtonStyled>
<button @click="dismissSurvey"><XIcon /> No thanks</button>
</ButtonStyled>
</div>
</div>
</transition>
<div
class="loading-indicator-container h-8 fixed z-50"
:style="{
@@ -672,7 +810,7 @@ function handleAuxClick(e) {
</div>
</div>
</div>
<!-- <template v-if="showAd">
<template v-if="showAd">
<a
href="https://modrinth.plus?app"
class="absolute bottom-[250px] w-full flex justify-center items-center gap-1 px-4 py-3 text-purple font-medium hover:underline z-10"
@@ -680,12 +818,12 @@ function handleAuxClick(e) {
>
<ArrowBigUpDashIcon class="text-2xl" /> Upgrade to Modrinth+
</a>
<PromotionWrapper />
</template> -->
<!-- <PromotionWrapper /> -->
</template>
</div>
</div>
<URLConfirmModal ref="urlModal" />
<Notifications ref="notificationsWrapper" sidebar />
<NotificationPanel has-sidebar />
<ErrorModal ref="errorModal" />
<ModInstallModal ref="modInstallModal" />
<IncompatibilityWarningModal ref="incompatibilityWarningModal" />
@@ -893,6 +1031,26 @@ function handleAuxClick(e) {
.sidebar-teleport-content:empty + .sidebar-default-content.sidebar-enabled {
display: contents;
}
.popup-survey-enter-active {
transition:
opacity 0.25s ease,
transform 0.25s cubic-bezier(0.51, 1.08, 0.35, 1.15);
transform-origin: top center;
}
.popup-survey-leave-active {
transition:
opacity 0.25s ease,
transform 0.25s cubic-bezier(0.68, -0.17, 0.23, 0.11);
transform-origin: top center;
}
.popup-survey-enter-from,
.popup-survey-leave-to {
opacity: 0;
transform: translateY(10rem) scale(0.8) scaleY(1.6);
}
</style>
<style>
.mac {
+12 -12
View File
@@ -1,18 +1,18 @@
export { default as ATLauncherIcon } from './atlauncher.svg'
export { default as BuyMeACoffeeIcon } from './bmac.svg'
export { default as DiscordIcon } from './discord.svg'
export { default as GDLauncherIcon } from './gdlauncher.png'
export { default as GithubIcon } from './github.svg'
export { default as GitLabIcon } from './gitlab.svg'
export { default as GoogleIcon } from './google.svg'
export { default as KoFiIcon } from './kofi.svg'
export { default as MastodonIcon } from './mastodon.svg'
export { default as MicrosoftIcon } from './microsoft.svg'
export { default as MultiMCIcon } from './multimc.webp'
export { default as OpenCollectiveIcon } from './opencollective.svg'
export { default as PatreonIcon } from './patreon.svg'
export { default as PaypalIcon } from './paypal.svg'
export { default as OpenCollectiveIcon } from './opencollective.svg'
export { default as TwitterIcon } from './twitter.svg'
export { default as GithubIcon } from './github.svg'
export { default as MastodonIcon } from './mastodon.svg'
export { default as RedditIcon } from './reddit.svg'
export { default as GoogleIcon } from './google.svg'
export { default as MicrosoftIcon } from './microsoft.svg'
export { default as SteamIcon } from './steam.svg'
export { default as GitLabIcon } from './gitlab.svg'
export { default as ATLauncherIcon } from './atlauncher.svg'
export { default as GDLauncherIcon } from './gdlauncher.png'
export { default as MultiMCIcon } from './multimc.webp'
export { default as PrismIcon } from './prism.svg'
export { default as RedditIcon } from './reddit.svg'
export { default as SteamIcon } from './steam.svg'
export { default as TwitterIcon } from './twitter.svg'
+6 -6
View File
@@ -1,12 +1,12 @@
export { default as SwapIcon } from './arrow-left-right.svg'
export { default as ToggleIcon } from './toggle.svg'
export { default as PackageIcon } from './package.svg'
export { default as VersionIcon } from './milestone.svg'
export { default as TextInputIcon } from './text-cursor-input.svg'
export { default as AddProjectImage } from './add-project.svg'
export { default as NewInstanceImage } from './new-instance.svg'
export { default as SwapIcon } from './arrow-left-right.svg'
export { default as MenuIcon } from './menu.svg'
export { default as ChatIcon } from './messages-square.svg'
export { default as Pirate } from './pirate.svg'
export { default as Microsoft } from './microsoft.svg'
export { default as PirateShip } from './pirate-ship.svg'
export { default as VersionIcon } from './milestone.svg'
export { default as NewInstanceImage } from './new-instance.svg'
export { default as PackageIcon } from './package.svg'
export { default as TextInputIcon } from './text-cursor-input.svg'
export { default as ToggleIcon } from './toggle.svg'
@@ -1,24 +1,26 @@
<script setup>
import Instance from '@/components/ui/Instance.vue'
import { computed, ref } from 'vue'
import {
ClipboardCopyIcon,
EyeIcon,
FolderOpenIcon,
PlayIcon,
PlusIcon,
TrashIcon,
StopCircleIcon,
EyeIcon,
SearchIcon,
StopCircleIcon,
TrashIcon,
XIcon,
} from '@modrinth/assets'
import { Button, DropdownSelect } from '@modrinth/ui'
import { Button, DropdownSelect, injectNotificationManager } from '@modrinth/ui'
import { formatCategoryHeader } from '@modrinth/utils'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import dayjs from 'dayjs'
import { duplicate, remove } from '@/helpers/profile.js'
import { handleError } from '@/store/notifications.js'
import { computed, ref } from 'vue'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import Instance from '@/components/ui/Instance.vue'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import { duplicate, remove } from '@/helpers/profile.js'
const { handleError } = injectNotificationManager()
const props = defineProps({
instances: {
@@ -1,5 +1,6 @@
<script setup>
import { computed, onBeforeUnmount, ref, watch } from 'vue'
import { useLoading } from '@/store/state.js'
const props = defineProps({
+26 -17
View File
@@ -1,31 +1,33 @@
<script setup>
import {
ClipboardCopyIcon,
FolderOpenIcon,
PlayIcon,
PlusIcon,
TrashIcon,
DownloadIcon,
GlobeIcon,
StopCircleIcon,
ExternalIcon,
EyeIcon,
FolderOpenIcon,
GlobeIcon,
PlayIcon,
PlusIcon,
StopCircleIcon,
TrashIcon,
} from '@modrinth/assets'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import Instance from '@/components/ui/Instance.vue'
import { HeadingLink, injectNotificationManager } from '@modrinth/ui'
import { openUrl } from '@tauri-apps/plugin-opener'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import ProjectCard from '@/components/ui/ProjectCard.vue'
import { get_by_profile_path } from '@/helpers/process.js'
import { handleError } from '@/store/notifications.js'
import { duplicate, kill, remove, run } from '@/helpers/profile.js'
import { useRouter } from 'vue-router'
import { showProfileInFolder } from '@/helpers/utils.js'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import Instance from '@/components/ui/Instance.vue'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import ProjectCard from '@/components/ui/ProjectCard.vue'
import { trackEvent } from '@/helpers/analytics'
import { get_by_profile_path } from '@/helpers/process.js'
import { duplicate, kill, remove, run } from '@/helpers/profile.js'
import { showProfileInFolder } from '@/helpers/utils.js'
import { handleSevereError } from '@/store/error.js'
import { install as installVersion } from '@/store/install.js'
import { openUrl } from '@tauri-apps/plugin-opener'
import { HeadingLink } from '@modrinth/ui'
const { handleError } = injectNotificationManager()
const router = useRouter()
@@ -163,7 +165,14 @@ const handleOptionsClick = async (args) => {
await navigator.clipboard.writeText(args.item.path)
break
case 'install': {
await installVersion(args.item.project_id, null, null, 'ProjectCardContextMenu')
await installVersion(
args.item.project_id,
null,
null,
'ProjectCardContextMenu',
() => {},
() => {},
).catch(handleError)
break
}
@@ -20,7 +20,8 @@
<Avatar size="xs" :src="avatarUrl" />
<div>
<h4>
<component :is="getAccountType(selectedAccount)" class="vector-icon" /> {{ selectedAccount.profile.name }}
<component :is="getAccountType(selectedAccount)" class="vector-icon" /> {{
selectedAccount.profile.name }}
</h4>
<p>Selected</p>
</div>
@@ -107,9 +108,7 @@
</div>
</div>
</ModalWrapper>
<ModalWrapper
ref="authenticationElybyErrorModal"
class="modal"
<ModalWrapper ref="authenticationElybyErrorModal" class="modal"
header="Error while proceeding authentication event with Ely.by">
<div class="flex flex-col gap-4 px-6 py-5">
<label class="text-base font-medium text-red-700">
@@ -181,7 +180,7 @@ import {
ElyByIcon,
SpinnerIcon
} from '@modrinth/assets'
import { Avatar, Button, Card } from '@modrinth/ui'
import { Avatar, Button, Card, injectNotificationManager } from '@modrinth/ui'
import { ref, computed, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
import {
elyby_auth_authenticate,
@@ -193,13 +192,14 @@ import {
login as login_flow,
get_default_user,
} from '@/helpers/auth'
import { handleError } from '@/store/state.js'
import { trackEvent } from '@/helpers/analytics'
import { process_listener } from '@/helpers/events'
import { handleSevereError } from '@/store/error.js'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { get_available_skins } from '@/helpers/skins'
import { getPlayerHeadUrl } from '@/helpers/rendering/batch-skin-renderer.ts'
import { get_available_skins } from '@/helpers/skins'
import { handleSevereError } from '@/store/error.js'
const { handleError } = injectNotificationManager()
defineProps({
mode: {
@@ -417,7 +417,7 @@ function setLoginDisabled(value) {
defineExpose({
refreshValues,
setLoginDisabled,
loginDisabled: microsoftLoginDisabled,
microsoftLoginDisabled,
})
await refreshValues()
@@ -1,11 +1,13 @@
<script setup lang="ts">
import { DropdownIcon, PlusIcon, FolderOpenIcon } from '@modrinth/assets'
import { ButtonStyled, OverflowMenu } from '@modrinth/ui'
import { DropdownIcon, FolderOpenIcon, PlusIcon } from '@modrinth/assets'
import { ButtonStyled, injectNotificationManager, OverflowMenu } from '@modrinth/ui'
import { open } from '@tauri-apps/plugin-dialog'
import { add_project_from_path } from '@/helpers/profile.js'
import { handleError } from '@/store/notifications.js'
import { useRouter } from 'vue-router'
import { add_project_from_path } from '@/helpers/profile.js'
const { handleError } = injectNotificationManager()
const props = defineProps({
instance: {
type: Object,
@@ -42,11 +42,12 @@
</template>
<script setup>
import { ChevronRightIcon, ChevronLeftIcon } from '@modrinth/assets'
import { ChevronLeftIcon, ChevronRightIcon } from '@modrinth/assets'
import { Button } from '@modrinth/ui'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { useRoute } from 'vue-router'
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { useBreadcrumbs } from '@/store/breadcrumbs'
const route = useRoute()
@@ -1,13 +1,21 @@
<template>
<transition name="fade">
<div v-show="shown" ref="contextMenu" class="context-menu" :style="{
<div
v-show="shown"
ref="contextMenu"
class="context-menu"
:style="{
left: left,
top: top,
}">
}"
>
<div v-for="(option, index) in options" :key="index" @click.stop="optionClicked(option.name)">
<hr v-if="option.type === 'divider'" class="divider" />
<div v-else-if="!(isLinkedData(item) && option.name === `add_content`)" class="item clickable"
:class="[option.color ?? 'base']">
<div
v-else-if="!(isLinkedData(item) && option.name === `add_content`)"
class="item clickable"
:class="[option.color ?? 'base']"
>
<slot :name="option.name" />
</div>
</div>
@@ -5,17 +5,9 @@
<p class="input-label">Profile Code</p>
<div class="iconified-input">
<SearchIcon aria-hidden="true" class="text-lg" />
<input
ref="codeInput"
v-model="profileCode"
autocomplete="off"
class="h-12 card-shadow"
spellcheck="false"
type="text"
placeholder="Enter CurseForge profile code"
maxlength="20"
@keyup.enter="importProfile"
/>
<input ref="codeInput" v-model="profileCode" autocomplete="off" class="h-12 card-shadow"
spellcheck="false" type="text" placeholder="Enter CurseForge profile code" maxlength="20"
@keyup.enter="importProfile" />
<Button v-if="profileCode" class="r-btn" @click="() => (profileCode = '')">
<XIcon />
</Button>
@@ -37,10 +29,7 @@
<span class="progress-percentage">{{ Math.floor(importProgress.percentage) }}%</span>
</div>
<div class="progress-bar-container">
<div
class="progress-bar"
:style="{ width: `${importProgress.percentage}%` }"
></div>
<div class="progress-bar" :style="{ width: `${importProgress.percentage}%` }"></div>
</div>
</div>
@@ -49,21 +38,12 @@
<XIcon />
Cancel
</Button>
<Button
v-if="!metadata"
@click="fetchMetadata"
:disabled="!profileCode.trim() || fetching"
color="secondary"
>
<Button v-if="!metadata" @click="fetchMetadata" :disabled="!profileCode.trim() || fetching"
color="secondary">
<SearchIcon v-if="!fetching" />
{{ fetching ? 'Checking...' : 'Check Profile' }}
</Button>
<Button
v-if="metadata"
@click="importProfile"
:disabled="importing"
color="primary"
>
<Button v-if="metadata" @click="importProfile" :disabled="importing" color="primary">
<DownloadIcon v-if="!importing" />
{{ importing ? 'Importing...' : 'Import Profile' }}
</Button>
@@ -86,7 +66,6 @@ import {
fetch_curseforge_profile_metadata,
import_curseforge_profile
} from '@/helpers/import.js'
import { handleError } from '@/store/notifications.js'
import { trackEvent } from '@/helpers/analytics'
import { loading_listener } from '@/helpers/events.js'
@@ -1,26 +1,30 @@
<script setup>
import {
CheckIcon,
CopyIcon,
DropdownIcon,
XIcon,
HammerIcon,
LogInIcon,
UpdatedIcon,
CopyIcon,
XIcon,
} from '@modrinth/assets'
import { ButtonStyled, Collapsible, injectNotificationManager } from '@modrinth/ui'
import { computed, ref } from 'vue'
import { ChatIcon } from '@/assets/icons'
import { ButtonStyled, Collapsible } from '@modrinth/ui'
import { ref, computed } from 'vue'
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
import { handleError } from '@/store/notifications.js'
import { handleSevereError } from '@/store/error.js'
import { cancel_directory_change } from '@/helpers/settings.ts'
import { install } from '@/helpers/profile.js'
import { trackEvent } from '@/helpers/analytics'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
import { install } from '@/helpers/profile.js'
import { cancel_directory_change } from '@/helpers/settings.ts'
import { handleSevereError } from '@/store/error.js'
// [AR] Feature
import { applyMigrationFix } from '@/helpers/utils.js'
import { restartApp } from '@/helpers/utils.js'
const { handleError } = injectNotificationManager()
const errorModal = ref()
const error = ref()
const closable = ref(true)
@@ -327,20 +331,10 @@ async function onApplyMigrationFix(eol) {
<template v-if="copied"> <CheckIcon class="text-green" /> Copied! </template>
<template v-else> <CopyIcon /> Copy debug info </template>
</button>
<ButtonStyled class="neon-button neon">
<a href="https://me.astralium.su/get/ar/help" target="_blank" rel="noopener noreferrer">
Get AstralRinth support
</a>
</ButtonStyled>
<ButtonStyled class="neon-button neon" >
<a href="https://me.astralium.su/get/ar" target="_blank" rel="noopener noreferrer">
Checkout latest releases
</a>
</ButtonStyled>
</ButtonStyled>
</div>
<template v-if="hasDebugInfo">
<div class="bg-button-bg rounded-xl mt-2 overflow-hidden">
<div class="bg-button-bg rounded-xl mt-2 overflow-clip">
<button
class="flex items-center justify-between w-full bg-transparent border-0 px-4 py-3 cursor-pointer"
@click="errorCollapsed = !errorCollapsed"
@@ -352,9 +346,7 @@ async function onApplyMigrationFix(eol) {
/>
</button>
<Collapsible :collapsed="errorCollapsed">
<pre
class="m-0 px-4 py-3 bg-bg rounded-none whitespace-pre-wrap break-words overflow-x-auto max-w-full"
>{{ debugInfo }}</pre>
<pre class="m-0 px-4 py-3 bg-bg rounded-none">{{ debugInfo }}</pre>
</Collapsible>
</div>
<template v-if="errorType === 'state_init'">
@@ -1,12 +1,14 @@
<script setup>
import { XIcon, PlusIcon } from '@modrinth/assets'
import { Button, Checkbox } from '@modrinth/ui'
import { PackageIcon, VersionIcon } from '@/assets/icons'
import { ref } from 'vue'
import { export_profile_mrpack, get_pack_export_candidates } from '@/helpers/profile.js'
import { PlusIcon, XIcon } from '@modrinth/assets'
import { Button, Checkbox, injectNotificationManager } from '@modrinth/ui'
import { open } from '@tauri-apps/plugin-dialog'
import { handleError } from '@/store/notifications.js'
import { ref } from 'vue'
import { PackageIcon, VersionIcon } from '@/assets/icons'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { export_profile_mrpack, get_pack_export_candidates } from '@/helpers/profile.js'
const { handleError } = injectNotificationManager()
const props = defineProps({
instance: {
@@ -1,6 +1,4 @@
<script setup>
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import {
DownloadIcon,
GameIcon,
@@ -9,17 +7,20 @@ import {
StopCircleIcon,
TimerIcon,
} from '@modrinth/assets'
import { Avatar, ButtonStyled, useRelativeTime } from '@modrinth/ui'
import { Avatar, ButtonStyled, injectNotificationManager, useRelativeTime } from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import { finish_install, kill, run } from '@/helpers/profile'
import { get_by_profile_path } from '@/helpers/process'
import dayjs from 'dayjs'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { trackEvent } from '@/helpers/analytics'
import { process_listener } from '@/helpers/events'
import { handleError } from '@/store/state.js'
import { get_by_profile_path } from '@/helpers/process'
import { finish_install, kill, run } from '@/helpers/profile'
import { showProfileInFolder } from '@/helpers/utils.js'
import { handleSevereError } from '@/store/error.js'
import { trackEvent } from '@/helpers/analytics'
import dayjs from 'dayjs'
const { handleError } = injectNotificationManager()
const formatRelativeTime = useRelativeTime()
const props = defineProps({
@@ -93,7 +94,7 @@ const stop = async (e, context) => {
const repair = async (e) => {
e?.stopPropagation()
await finish_install(props.instance)
await finish_install(props.instance).catch(handleError)
}
const openFolder = async () => {
@@ -206,8 +206,6 @@
</template>
<script setup>
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import CurseForgeProfileImportModal from '@/components/ui/CurseForgeProfileImportModal.vue'
import {
CodeIcon,
FolderOpenIcon,
@@ -218,24 +216,29 @@ import {
UploadIcon,
XIcon,
} from '@modrinth/assets'
import { Avatar, Button, Checkbox, Chips } from '@modrinth/ui'
import { computed, onUnmounted, ref, shallowRef } from 'vue'
import { get_loaders } from '@/helpers/tags'
import { create } from '@/helpers/profile'
import { open } from '@tauri-apps/plugin-dialog'
import { Avatar, Button, Checkbox, Chips, injectNotificationManager } from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
import { handleError } from '@/store/notifications.js'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { open } from '@tauri-apps/plugin-dialog'
import { computed, onUnmounted, ref, shallowRef } from 'vue'
import Multiselect from 'vue-multiselect'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import ProgressBar from '@/components/ui/ProgressBar.vue'
import { trackEvent } from '@/helpers/analytics'
import { create_profile_and_install_from_file } from '@/helpers/pack.js'
import {
get_default_launcher_path,
get_importable_instances,
import_instance,
} from '@/helpers/import.js'
import ProgressBar from '@/components/ui/ProgressBar.vue'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
import { create_profile_and_install_from_file } from '@/helpers/pack.js'
import { create } from '@/helpers/profile'
import { get_loaders } from '@/helpers/tags'
import CurseForgeProfileImportModal from '@/components/ui/CurseForgeProfileImportModal.vue'
const { handleError } = injectNotificationManager()
const profile_name = ref('')
const game_version = ref('')
@@ -1,8 +1,8 @@
<script setup lang="ts">
import { convertFileSrc } from '@tauri-apps/api/core'
import { formatCategory } from '@modrinth/utils'
import { GameIcon, LeftArrowIcon } from '@modrinth/assets'
import { Avatar, ButtonStyled } from '@modrinth/ui'
import { formatCategory } from '@modrinth/utils'
import { convertFileSrc } from '@tauri-apps/api/core'
type Instance = {
game_version: string
@@ -35,13 +35,15 @@
</ModalWrapper>
</template>
<script setup>
import { PlusIcon, CheckIcon, XIcon } from '@modrinth/assets'
import { Button } from '@modrinth/ui'
import { CheckIcon, PlusIcon, XIcon } from '@modrinth/assets'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { ref } from 'vue'
import { find_filtered_jres } from '@/helpers/jre.js'
import { handleError } from '@/store/notifications.js'
import { trackEvent } from '@/helpers/analytics'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { find_filtered_jres } from '@/helpers/jre.js'
const { handleError } = injectNotificationManager()
const chosenInstallOptions = ref([])
const detectJavaModal = ref(null)
@@ -53,20 +53,22 @@
<script setup>
import {
SearchIcon,
PlayIcon,
CheckIcon,
XIcon,
FolderSearchIcon,
DownloadIcon,
FolderSearchIcon,
PlayIcon,
SearchIcon,
XIcon,
} from '@modrinth/assets'
import { Button } from '@modrinth/ui'
import { auto_install_java, find_filtered_jres, get_jre, test_jre } from '@/helpers/jre.js'
import { ref } from 'vue'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { open } from '@tauri-apps/plugin-dialog'
import { ref } from 'vue'
import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue'
import { handleError } from '@/store/state.js'
import { trackEvent } from '@/helpers/analytics'
import { auto_install_java, find_filtered_jres, get_jre, test_jre } from '@/helpers/jre.js'
const { handleError } = injectNotificationManager()
const props = defineProps({
version: {
@@ -1,11 +1,12 @@
<script setup>
import { CheckIcon } from '@modrinth/assets'
import { Button, Badge } from '@modrinth/ui'
import { Badge, Button } from '@modrinth/ui'
import { computed, ref } from 'vue'
import { update_managed_modrinth_version } from '@/helpers/profile'
import { releaseColor } from '@/helpers/utils'
import { SwapIcon } from '@/assets/icons/index.js'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { update_managed_modrinth_version } from '@/helpers/profile'
import { releaseColor } from '@/helpers/utils'
const props = defineProps({
versions: {
@@ -30,7 +30,7 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import type { RouteLocationRaw } from 'vue-router'
import { useRoute, RouterLink } from 'vue-router'
import { RouterLink, useRoute } from 'vue-router'
const route = useRoute()
@@ -1,10 +1,10 @@
<script setup>
import { Avatar, TagItem } from '@modrinth/ui'
import { DownloadIcon, HeartIcon, TagIcon } from '@modrinth/assets'
import { formatNumber, formatCategory } from '@modrinth/utils'
import { computed } from 'vue'
import { Avatar, TagItem } from '@modrinth/ui'
import { formatCategory, formatNumber } from '@modrinth/utils'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { computed } from 'vue'
import { useRouter } from 'vue-router'
dayjs.extend(relativeTime)
@@ -1,13 +1,15 @@
<script setup>
import { list } from '@/helpers/profile'
import { handleError } from '@/store/notifications'
import { SpinnerIcon } from '@modrinth/assets'
import { Avatar, injectNotificationManager } from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import dayjs from 'dayjs'
import { onUnmounted, ref } from 'vue'
import { profile_listener } from '@/helpers/events.js'
import NavButton from '@/components/ui/NavButton.vue'
import { Avatar } from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import { SpinnerIcon } from '@modrinth/assets'
import { profile_listener } from '@/helpers/events.js'
import { list } from '@/helpers/profile'
const { handleError } = injectNotificationManager()
const recentInstances = ref([])
const getInstances = async () => {
@@ -20,12 +20,21 @@
>
{{ selectedProcess.profile.name }}
</router-link>
<div v-if="currentProcesses.length > 1" class="arrow button-base" :class="{ rotate: showProfiles }"
@click="toggleProfiles()">
<div
v-if="currentProcesses.length > 1"
class="arrow button-base"
:class="{ rotate: showProfiles }"
@click="toggleProfiles()"
>
<DropdownIcon />
</div>
</div>
<Button v-tooltip="'Stop instance'" icon-only class="icon-button stop" @click="stop(selectedProcess)">
<Button
v-tooltip="'Stop instance'"
icon-only
class="icon-button stop"
@click="stop(selectedProcess)"
>
<StopCircleIcon />
</Button>
<Button v-tooltip="'View logs'" icon-only class="icon-button" @click="goToTerminal()">
@@ -45,20 +54,39 @@
</h3>
<ProgressBar :progress="Math.floor((100 * loadingBar.current) / loadingBar.total)" />
<div class="row">
{{ Math.floor((100 * loadingBar.current) / loadingBar.total) }}% {{ loadingBar.message }}
{{ Math.floor((100 * loadingBar.current) / loadingBar.total) }}%
{{ loadingBar.message }}
</div>
</div>
</Card>
</transition>
<transition name="download">
<Card v-if="showProfiles === true && currentProcesses.length > 0" ref="profiles" class="profile-card">
<Button v-for="process in currentProcesses" :key="process.uuid" class="profile-button"
@click="selectProcess(process)">
<Card
v-if="showProfiles === true && currentProcesses.length > 0"
ref="profiles"
class="profile-card"
>
<Button
v-for="process in currentProcesses"
:key="process.uuid"
class="profile-button"
@click="selectProcess(process)"
>
<div class="text"><span class="circle running" /> {{ process.profile.name }}</div>
<Button v-tooltip="'Stop instance'" icon-only class="icon-button stop" @click.stop="stop(process)">
<Button
v-tooltip="'Stop instance'"
icon-only
class="icon-button stop"
@click.stop="stop(process)"
>
<StopCircleIcon />
</Button>
<Button v-tooltip="'View logs'" icon-only class="icon-button" @click.stop="goToTerminal(process.profile.path)">
<Button
v-tooltip="'View logs'"
icon-only
class="icon-button"
@click.stop="goToTerminal(process.profile.path)"
>
<TerminalSquareIcon />
</Button>
</Button>
@@ -69,21 +97,23 @@
<script setup>
import {
DownloadIcon,
DropdownIcon,
StopCircleIcon,
TerminalSquareIcon,
DropdownIcon,
UnplugIcon,
} from '@modrinth/assets'
import { Button, ButtonStyled, Card } from '@modrinth/ui'
import { Button, ButtonStyled, Card, injectNotificationManager } from '@modrinth/ui'
import { onBeforeUnmount, onMounted, ref } from 'vue'
import { get_all as getRunningProcesses, kill as killProcess } from '@/helpers/process'
import { loading_listener, process_listener } from '@/helpers/events'
import { useRouter } from 'vue-router'
import { progress_bars_list } from '@/helpers/state.js'
import ProgressBar from '@/components/ui/ProgressBar.vue'
import { handleError } from '@/store/notifications.js'
import { get_many } from '@/helpers/profile.js'
import { trackEvent } from '@/helpers/analytics'
import { loading_listener, process_listener } from '@/helpers/events'
import { get_all as getRunningProcesses, kill as killProcess } from '@/helpers/process'
import { get_many } from '@/helpers/profile.js'
import { progress_bars_list } from '@/helpers/state.js'
const { handleError } = injectNotificationManager()
const router = useRouter()
const card = ref(null)
@@ -252,7 +282,6 @@ onBeforeUnmount(() => {
transition: transform 0.2s ease-in-out;
display: flex;
align-items: center;
&.rotate {
transform: rotate(180deg);
}
@@ -274,10 +303,8 @@ onBeforeUnmount(() => {
gap: var(--gap-xs);
white-space: nowrap;
overflow: hidden;
-webkit-user-select: none;
/* Safari */
-ms-user-select: none;
/* IE 10 and IE 11 */
-webkit-user-select: none; /* Safari */
-ms-user-select: none; /* IE 10 and IE 11 */
user-select: none;
&.clickable:hover {
@@ -117,16 +117,19 @@
</template>
<script setup>
import { TagsIcon, DownloadIcon, HeartIcon, PlusIcon, CheckIcon } from '@modrinth/assets'
import { ButtonStyled, Avatar } from '@modrinth/ui'
import { formatNumber, formatCategory } from '@modrinth/utils'
import { CheckIcon, DownloadIcon, HeartIcon, PlusIcon, TagsIcon } from '@modrinth/assets'
import { Avatar, ButtonStyled, injectNotificationManager } from '@modrinth/ui'
import { formatCategory, formatNumber } from '@modrinth/utils'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { ref, computed } from 'vue'
import { install as installVersion } from '@/store/install.js'
import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'
import { install as installVersion } from '@/store/install.js'
dayjs.extend(relativeTime)
const { handleError } = injectNotificationManager()
const router = useRouter()
const props = defineProps({
@@ -174,7 +177,7 @@ async function install() {
(profile) => {
router.push(`/instance/${profile}`)
},
)
).catch(handleError)
}
const modpack = computed(() => props.project.project_type === 'modpack')
@@ -82,11 +82,12 @@
</template>
<script setup>
import { MaximizeIcon, MinimizeIcon, XIcon } from '@modrinth/assets'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { ref, watch } from 'vue'
import ProgressBar from '@/components/ui/ProgressBar.vue'
import { loading_listener } from '@/helpers/events.js'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { XIcon, MaximizeIcon, MinimizeIcon } from '@modrinth/assets'
import { getOS } from '@/helpers/utils.js'
import { useLoading } from '@/store/loading.js'
@@ -1,12 +1,14 @@
<script setup>
import { Button } from '@modrinth/ui'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { ref } from 'vue'
import SearchCard from '@/components/ui/SearchCard.vue'
import { get_categories } from '@/helpers/tags.js'
import { handleError } from '@/store/notifications.js'
import { get_version, get_project } from '@/helpers/cache.js'
import { install as installVersion } from '@/store/install.js'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import SearchCard from '@/components/ui/SearchCard.vue'
import { get_project, get_version } from '@/helpers/cache.js'
import { get_categories } from '@/helpers/tags.js'
import { install as installVersion } from '@/store/install.js'
const { handleError } = injectNotificationManager()
const confirmModal = ref(null)
const project = ref(null)
@@ -37,7 +39,14 @@ defineExpose({
async function install() {
confirmModal.value.hide()
await installVersion(project.value.id, version.value.id, null, 'URLConfirmModal')
await installVersion(
project.value.id,
version.value.id,
null,
'URLConfirmModal',
() => {},
() => {},
).catch(handleError)
}
</script>
@@ -1,23 +1,30 @@
<script setup lang="ts">
import { Avatar, ButtonStyled, OverflowMenu, useRelativeTime } from '@modrinth/ui'
import {
UserPlusIcon,
MoreVerticalIcon,
MailIcon,
MoreVerticalIcon,
SettingsIcon,
TrashIcon,
UserPlusIcon,
XIcon,
} from '@modrinth/assets'
import { ref, onUnmounted, watch, computed } from 'vue'
import { friend_listener } from '@/helpers/events'
import { friends, friend_statuses, add_friend, remove_friend } from '@/helpers/friends'
import { get_user_many } from '@/helpers/cache'
import { handleError } from '@/store/notifications.js'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import {
Avatar,
ButtonStyled,
injectNotificationManager,
OverflowMenu,
useRelativeTime,
} from '@modrinth/ui'
import type { Dayjs } from 'dayjs'
import dayjs from 'dayjs'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { computed, onUnmounted, ref, watch } from 'vue'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { get_user_many } from '@/helpers/cache'
import { friend_listener } from '@/helpers/events'
import { add_friend, friend_statuses, friends, remove_friend } from '@/helpers/friends'
const { handleError } = injectNotificationManager()
const formatRelativeTime = useRelativeTime()
const props = defineProps<{
@@ -56,16 +56,18 @@
</template>
<script setup>
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { XIcon, DownloadIcon } from '@modrinth/assets'
import { Button } from '@modrinth/ui'
import { DownloadIcon, XIcon } from '@modrinth/assets'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { formatCategory } from '@modrinth/utils'
import { add_project_from_version as installMod } from '@/helpers/profile'
import { ref } from 'vue'
import { handleError } from '@/store/state.js'
import { trackEvent } from '@/helpers/analytics'
import Multiselect from 'vue-multiselect'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { add_project_from_version as installMod } from '@/helpers/profile'
const { handleError } = injectNotificationManager()
const instance = ref(null)
const project = ref(null)
const versions = ref(null)
@@ -1,11 +1,13 @@
<script setup>
import { DownloadIcon, XIcon } from '@modrinth/assets'
import { Button } from '@modrinth/ui'
import { create_profile_and_install as pack_install } from '@/helpers/pack'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { ref } from 'vue'
import { trackEvent } from '@/helpers/analytics'
import { handleError } from '@/store/state.js'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { create_profile_and_install as pack_install } from '@/helpers/pack'
const { handleError } = injectNotificationManager()
const versionId = ref()
const project = ref()
@@ -1,29 +1,30 @@
<script setup>
import {
CheckIcon,
DownloadIcon,
PlusIcon,
RightArrowIcon,
UploadIcon,
XIcon,
RightArrowIcon,
CheckIcon,
} from '@modrinth/assets'
import { Avatar, Button, Card } from '@modrinth/ui'
import { Avatar, Button, Card, injectNotificationManager } from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import { open } from '@tauri-apps/plugin-dialog'
import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import {
add_project_from_version as installMod,
check_installed,
create,
get,
list,
create,
} from '@/helpers/profile'
import { open } from '@tauri-apps/plugin-dialog'
import { installVersionDependencies } from '@/store/install.js'
import { handleError } from '@/store/notifications.js'
import { useRouter } from 'vue-router'
import { convertFileSrc } from '@tauri-apps/api/core'
import { trackEvent } from '@/helpers/analytics'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
const { handleError } = injectNotificationManager()
const router = useRouter()
const versions = ref()
@@ -109,7 +110,7 @@ async function install(instance) {
}
await installMod(instance.path, version.id).catch(handleError)
await installVersionDependencies(instance, version)
await installVersionDependencies(instance, version).catch(handleError)
instance.installedMod = true
instance.installing = false
@@ -184,7 +185,7 @@ const createInstance = async () => {
await router.push(`/instance/${encodeURIComponent(id)}/`)
const instance = await get(id, true)
await installVersionDependencies(instance, versions.value[0])
await installVersionDependencies(instance, versions.value[0]).catch(handleError)
trackEvent('InstanceCreate', {
profile_name: name.value,
@@ -1,17 +1,25 @@
<script setup lang="ts">
import { CopyIcon, EditIcon, PlusIcon, SpinnerIcon, TrashIcon, UploadIcon } from '@modrinth/assets'
import {
Avatar,
ButtonStyled,
Checkbox,
injectNotificationManager,
OverflowMenu,
} from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import { SpinnerIcon, TrashIcon, UploadIcon, PlusIcon, EditIcon, CopyIcon } from '@modrinth/assets'
import { Avatar, ButtonStyled, OverflowMenu, Checkbox } from '@modrinth/ui'
import { computed, ref, type Ref, watch } from 'vue'
import { duplicate, edit, edit_icon, list, remove } from '@/helpers/profile'
import { handleError } from '@/store/notifications'
import { trackEvent } from '@/helpers/analytics'
import { open } from '@tauri-apps/plugin-dialog'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { computed, type Ref, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import type { InstanceSettingsTabProps, GameInstance } from '../../../helpers/types'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import { trackEvent } from '@/helpers/analytics'
import { duplicate, edit, edit_icon, list, remove } from '@/helpers/profile'
import type { GameInstance, InstanceSettingsTabProps } from '../../../helpers/types'
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const router = useRouter()
@@ -1,12 +1,14 @@
<script setup lang="ts">
import { Checkbox } from '@modrinth/ui'
import { computed, ref, watch } from 'vue'
import { handleError } from '@/store/notifications'
import { Checkbox, injectNotificationManager } from '@modrinth/ui'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { get } from '@/helpers/settings.ts'
import { edit } from '@/helpers/profile'
import type { InstanceSettingsTabProps, AppSettings, Hooks } from '../../../helpers/types'
import { computed, ref, watch } from 'vue'
import { edit } from '@/helpers/profile'
import { get } from '@/helpers/settings.ts'
import type { AppSettings, Hooks, InstanceSettingsTabProps } from '../../../helpers/types'
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const props = defineProps<InstanceSettingsTabProps>()
@@ -1,23 +1,23 @@
<script setup lang="ts">
import {
TransferIcon,
IssuesIcon,
HammerIcon,
DownloadIcon,
WrenchIcon,
UndoIcon,
HammerIcon,
IssuesIcon,
SpinnerIcon,
UnplugIcon,
TransferIcon,
UndoIcon,
UnlinkIcon,
UnplugIcon,
WrenchIcon,
} from '@modrinth/assets'
import { Avatar, Checkbox, Chips, ButtonStyled, TeleportDropdownMenu } from '@modrinth/ui'
import { computed, type ComputedRef, type Ref, ref, shallowRef, watch } from 'vue'
import { edit, install, update_repair_modrinth } from '@/helpers/profile'
import { handleError } from '@/store/notifications'
import { trackEvent } from '@/helpers/analytics'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { get_loader_versions } from '@/helpers/metadata'
import { get_game_versions, get_loaders } from '@/helpers/tags'
import {
Avatar,
ButtonStyled,
Checkbox,
Chips,
injectNotificationManager,
TeleportDropdownMenu,
} from '@modrinth/ui'
import {
formatCategory,
type GameVersionTag,
@@ -25,15 +25,23 @@ import {
type Project,
type Version,
} from '@modrinth/utils'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { get_project, get_version_many } from '@/helpers/cache'
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
import { defineMessages, useVIntl } from '@vintl/vintl'
import dayjs from 'dayjs'
import { computed, type ComputedRef, type Ref, ref, shallowRef, watch } from 'vue'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
import { trackEvent } from '@/helpers/analytics'
import { get_project, get_version_many } from '@/helpers/cache'
import { get_loader_versions } from '@/helpers/metadata'
import { edit, install, update_repair_modrinth } from '@/helpers/profile'
import { get_game_versions, get_loaders } from '@/helpers/tags'
import type {
InstanceSettingsTabProps,
ManifestLoaderVersion,
Manifest,
ManifestLoaderVersion,
} from '../../../helpers/types'
import { initAuthlibPatching } from '@/helpers/utils.js'
@@ -41,6 +49,7 @@ const authLibPatchingModal = ref(null)
const isAuthLibPatchedSuccess = ref(false)
const isAuthLibPatching = ref(false)
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const repairConfirmModal = ref()
@@ -545,7 +554,8 @@ async function handleInitAuthLibPatching(ismojang: boolean) {
</div>
<div v-else-if="!modpackProject && instance.linked_data && !fetching" class="mb-2">
<p class="text-brand-red font-medium mt-0">
<IssuesIcon class="top-[3px] relative" /> {{ formatMessage(messages.noModpackFound) }}
<IssuesIcon class="top-[3px] relative" />
{{ formatMessage(messages.noModpackFound) }}
</p>
<p>{{ formatMessage(messages.debugInformation) }}</p>
<div class="bg-bg p-6 rounded-2xl mt-2 text-sm text-secondary">
@@ -572,7 +582,9 @@ async function handleInitAuthLibPatching(ismojang: boolean) {
{{
modpackProject
? modpackProject.title
: formatMessage(messages.minecraftVersion, { version: instance.game_version })
: formatMessage(messages.minecraftVersion, {
version: instance.game_version,
})
}}
</span>
<span class="text-sm text-secondary leading-none">
@@ -702,7 +714,12 @@ async function handleInitAuthLibPatching(ismojang: boolean) {
/>
<div v-else class="mt-2 text-brand-red flex gap-2 items-center">
<IssuesIcon />
{{ formatMessage(messages.noLoaderVersions, { loader: loader, version: gameVersion }) }}
{{
formatMessage(messages.noLoaderVersions, {
loader: loader,
version: gameVersion,
})
}}
</div>
</template>
<div class="mt-4 flex flex-wrap gap-2">
@@ -1,20 +1,22 @@
<script setup lang="ts">
import { Checkbox, Slider } from '@modrinth/ui'
import { CheckCircleIcon, XCircleIcon } from '@modrinth/assets'
import { computed, readonly, ref, watch } from 'vue'
import { edit, get_optimal_jre_key } from '@/helpers/profile'
import { handleError } from '@/store/notifications'
import { Checkbox, injectNotificationManager, Slider } from '@modrinth/ui'
import { defineMessages, useVIntl } from '@vintl/vintl'
import JavaSelector from '@/components/ui/JavaSelector.vue'
import { get } from '@/helpers/settings.ts'
import type { InstanceSettingsTabProps, AppSettings, MemorySettings } from '../../../helpers/types'
import useMemorySlider from '@/composables/useMemorySlider'
import { computed, readonly, ref, watch } from 'vue'
import JavaSelector from '@/components/ui/JavaSelector.vue'
import useMemorySlider from '@/composables/useMemorySlider'
import { edit, get_optimal_jre_key } from '@/helpers/profile'
import { get } from '@/helpers/settings.ts'
import type { AppSettings, InstanceSettingsTabProps, MemorySettings } from '../../../helpers/types'
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const props = defineProps<InstanceSettingsTabProps>()
const globalSettings = (await get().catch(handleError)) as AppSettings
const globalSettings = (await get().catch(handleError)) as unknown as AppSettings
const overrideJavaInstall = ref(!!props.instance.java_path)
const optimalJava = readonly(await get_optimal_jre_key(props.instance.path).catch(handleError))
@@ -34,7 +36,10 @@ const envVars = ref(
const overrideMemorySettings = ref(!!props.instance.memory)
const memory = ref(props.instance.memory ?? globalSettings.memory)
const { maxMemory, snapPoints } = await useMemorySlider()
const { maxMemory, snapPoints } = (await useMemorySlider().catch(handleError)) as unknown as {
maxMemory: number
snapPoints: number[]
}
const editProfileObject = computed(() => {
const editProfile: {
@@ -1,12 +1,14 @@
<script setup lang="ts">
import { Checkbox, Toggle } from '@modrinth/ui'
import { computed, ref, type Ref, watch } from 'vue'
import { handleError } from '@/store/notifications'
import { Checkbox, injectNotificationManager, Toggle } from '@modrinth/ui'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { get } from '@/helpers/settings.ts'
import { computed, type Ref, ref, watch } from 'vue'
import { edit } from '@/helpers/profile'
import { get } from '@/helpers/settings.ts'
import type { AppSettings, InstanceSettingsTabProps } from '../../../helpers/types'
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const props = defineProps<InstanceSettingsTabProps>()
@@ -1,29 +1,29 @@
<script setup lang="ts">
import {
ReportIcon,
AstralRinthLogo,
ShieldIcon,
SettingsIcon,
GaugeIcon,
PaintbrushIcon,
GameIcon,
CoffeeIcon,
GameIcon,
GaugeIcon,
AstralRinthLogo,
DownloadIcon,
SpinnerIcon,
PaintbrushIcon,
ReportIcon,
SettingsIcon,
ShieldIcon,
} from '@modrinth/assets'
import { TabbedModal } from '@modrinth/ui'
import { computed, ref, watch } from 'vue'
import { useVIntl, defineMessage } from '@vintl/vintl'
import AppearanceSettings from '@/components/ui/settings/AppearanceSettings.vue'
import JavaSettings from '@/components/ui/settings/JavaSettings.vue'
import ResourceManagementSettings from '@/components/ui/settings/ResourceManagementSettings.vue'
import PrivacySettings from '@/components/ui/settings/PrivacySettings.vue'
import DefaultInstanceSettings from '@/components/ui/settings/DefaultInstanceSettings.vue'
import { getVersion } from '@tauri-apps/api/app'
import { version as getOsVersion, platform as getOsPlatform } from '@tauri-apps/plugin-os'
import { useTheming } from '@/store/state'
import FeatureFlagSettings from '@/components/ui/settings/FeatureFlagSettings.vue'
import { platform as getOsPlatform, version as getOsVersion } from '@tauri-apps/plugin-os'
import { defineMessage, useVIntl } from '@vintl/vintl'
import { computed, ref, watch } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import AppearanceSettings from '@/components/ui/settings/AppearanceSettings.vue'
import DefaultInstanceSettings from '@/components/ui/settings/DefaultInstanceSettings.vue'
import FeatureFlagSettings from '@/components/ui/settings/FeatureFlagSettings.vue'
import JavaSettings from '@/components/ui/settings/JavaSettings.vue'
import PrivacySettings from '@/components/ui/settings/PrivacySettings.vue'
import ResourceManagementSettings from '@/components/ui/settings/ResourceManagementSettings.vue'
import { get, set } from '@/helpers/settings.ts'
// [AR] Imports
import { installState, getRemote, updateState } from '@/helpers/update.js'
@@ -42,6 +42,7 @@ const initDownload = async () => {
updateRequestFailView.value.show()
}
}
import { useTheming } from '@/store/state'
const themeStore = useTheming()
@@ -159,12 +160,16 @@ function devModeCount() {
<div class="flex items-center gap-3">
<button
class="p-0 m-0 bg-transparent border-none cursor-pointer button-animation"
:class="{ 'text-brand': themeStore.devMode, 'text-secondary': !themeStore.devMode }"
@click="devModeCount">
:class="{
'text-brand': themeStore.devMode,
'text-secondary': !themeStore.devMode,
}"
@click="devModeCount"
>
<AstralRinthLogo class="w-6 h-6" />
</button>
<div>
<p class="m-0">AstralRinth App {{ version }}</p>
<p class="m-0">Modrinth App {{ version }}</p>
<p class="m-0">
<span v-if="osPlatform === 'macos'">MacOS</span>
<span v-else class="capitalize">{{ osPlatform }}</span>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { LogInIcon, SpinnerIcon } from '@modrinth/assets'
import { ref } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
defineProps({
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ConfirmModal } from '@modrinth/ui'
import { ref } from 'vue'
import { useTheming } from '@/store/theme.js'
// import { hide_ads_window, show_ads_window } from '@/helpers/ads.js'
import { useTheming } from '@/store/theme.ts'
const themeStore = useTheming()
@@ -52,10 +53,11 @@ const modal = ref(null)
defineExpose({
show: () => {
// hide_ads_window()
modal.value.show()
},
hide: () => {
// onModalHide()
onModalHide()
modal.value.hide()
},
})
@@ -2,6 +2,7 @@
import { ChevronRightIcon } from '@modrinth/assets'
import { Avatar } from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import type { GameInstance } from '@/helpers/types'
defineProps<{
@@ -1,22 +1,24 @@
<script setup lang="ts">
import {
ChevronRightIcon,
CodeIcon,
CoffeeIcon,
InfoIcon,
WrenchIcon,
MonitorIcon,
CodeIcon,
WrenchIcon,
} from '@modrinth/assets'
import { Avatar, TabbedModal, type TabbedModalTab } from '@modrinth/ui'
import { ref } from 'vue'
import { defineMessage, useVIntl } from '@vintl/vintl'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import GeneralSettings from '@/components/ui/instance_settings/GeneralSettings.vue'
import { convertFileSrc } from '@tauri-apps/api/core'
import { defineMessage, useVIntl } from '@vintl/vintl'
import { ref } from 'vue'
import GeneralSettings from '@/components/ui/instance_settings/GeneralSettings.vue'
import HooksSettings from '@/components/ui/instance_settings/HooksSettings.vue'
import InstallationSettings from '@/components/ui/instance_settings/InstallationSettings.vue'
import JavaSettings from '@/components/ui/instance_settings/JavaSettings.vue'
import WindowSettings from '@/components/ui/instance_settings/WindowSettings.vue'
import HooksSettings from '@/components/ui/instance_settings/HooksSettings.vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import type { InstanceSettingsTabProps } from '../../../helpers/types'
const { formatMessage } = useVIntl()
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { useTemplateRef } from 'vue'
import { NewModal as Modal } from '@modrinth/ui'
// import { show_ads_window, hide_ads_window } from '@/helpers/ads.js'
import { useTheming } from '@/store/theme.js'
import { useTemplateRef } from 'vue'
// import { hide_ads_window, show_ads_window } from '@/helpers/ads.js'
import { useTheming } from '@/store/theme.ts'
const themeStore = useTheming()
@@ -18,7 +19,7 @@ const props = defineProps({
onHide: {
type: Function,
default() {
return () => { }
return () => {}
},
},
// showAdOnClose: {
@@ -40,9 +41,9 @@ defineExpose({
})
function onModalHide() {
// if (props.showAdOnClose) {
// show_ads_window()
// }
// if (props.showAdOnClose) {
// show_ads_window()
// }
props.onHide?.()
}
</script>
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ShareModal } from '@modrinth/ui'
import { ref } from 'vue'
import { useTheming } from '@/store/theme.js'
// import { hide_ads_window, show_ads_window } from '@/helpers/ads.js'
import { useTheming } from '@/store/theme.ts'
const themeStore = useTheming()
@@ -33,6 +34,7 @@ const modal = ref(null)
defineExpose({
show: (passedContent) => {
// hide_ads_window()
modal.value.show(passedContent)
},
hide: () => {
@@ -40,9 +42,21 @@ defineExpose({
modal.value.hide()
},
})
// function onModalHide() {
// show_ads_window()
// }
</script>
<template>
<ShareModal ref="modal" :header="header" :share-title="shareTitle" :share-text="shareText" :link="link"
:open-in-new-tab="openInNewTab" :on-hide="onModalHide" :noblur="!themeStore.advancedRendering" />
<ShareModal
ref="modal"
:header="header"
:share-title="shareTitle"
:share-text="shareText"
:link="link"
:open-in-new-tab="openInNewTab"
:on-hide="onModalHide"
:noblur="!themeStore.advancedRendering"
/>
</template>
@@ -1,9 +1,10 @@
<script setup lang="ts">
import { TeleportDropdownMenu, ThemeSelector, Toggle } from '@modrinth/ui'
import { useTheming } from '@/store/state'
import { get, set } from '@/helpers/settings.ts'
import { ref, watch } from 'vue'
import { get, set } from '@/helpers/settings.ts'
import { getOS } from '@/helpers/utils'
import { useTheming } from '@/store/state'
import type { ColorTheme } from '@/store/theme.ts'
const themeStore = useTheming()
@@ -1,8 +1,11 @@
<script setup lang="ts">
import { get, set } from '@/helpers/settings.ts'
import { injectNotificationManager, Slider, Toggle } from '@modrinth/ui'
import { ref, watch } from 'vue'
import { Slider, Toggle } from '@modrinth/ui'
import useMemorySlider from '@/composables/useMemorySlider'
import { get, set } from '@/helpers/settings.ts'
const { handleError } = injectNotificationManager()
const fetchSettings = await get()
fetchSettings.launchArgs = fetchSettings.extra_launch_args.join(' ')
@@ -10,7 +13,10 @@ fetchSettings.envVars = fetchSettings.custom_env_vars.map((x) => x.join('=')).jo
const settings = ref(fetchSettings)
const { maxMemory, snapPoints } = await useMemorySlider()
const { maxMemory, snapPoints } = (await useMemorySlider().catch(handleError)) as unknown as {
maxMemory: number
snapPoints: number[]
}
watch(
settings,
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { Toggle } from '@modrinth/ui'
import { useTheming } from '@/store/state'
import { ref, watch } from 'vue'
import { get as getSettings, set as setSettings } from '@/helpers/settings.ts'
import { useTheming } from '@/store/state'
import { DEFAULT_FEATURE_FLAGS, type FeatureFlag } from '@/store/theme.ts'
const themeStore = useTheming()
@@ -1,8 +1,11 @@
<script setup>
import { injectNotificationManager } from '@modrinth/ui'
import { ref } from 'vue'
import { get_java_versions, set_java_version } from '@/helpers/jre'
import { handleError } from '@/store/notifications'
import JavaSelector from '@/components/ui/JavaSelector.vue'
import { get_java_versions, set_java_version } from '@/helpers/jre'
const { handleError } = injectNotificationManager()
const javaVersions = ref(await get_java_versions().catch(handleError))
async function updateJavaVersion(version) {
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import { get, set } from '@/helpers/settings.ts'
import { Toggle } from '@modrinth/ui'
import { ref, watch } from 'vue'
import { optInAnalytics, optOutAnalytics } from '@/helpers/analytics'
import { get, set } from '@/helpers/settings.ts'
const settings = ref(await get())
@@ -26,7 +27,7 @@ watch(
<div>
<h2 class="m-0 text-lg font-extrabold text-contrast">Personalized ads</h2>
<p class="m-0 text-sm">
(Hard disabled by AR) Modrinth's ad provider, Aditude, shows ads based on your preferences. By disabling this
Modrinth's ad provider, Aditude, shows ads based on your preferences. By disabling this
option, you opt out and ads will no longer be shown based on your interests.
</p>
</div>
@@ -38,7 +39,7 @@ watch(
<div>
<h2 class="m-0 text-lg font-extrabold text-contrast">Telemetry</h2>
<p class="m-0 text-sm">
(Hard disabled by AR) Modrinth collects anonymized analytics and usage data to improve our user experience and
Modrinth collects anonymized analytics and usage data to improve our user experience and
customize your experience. By disabling this option, you opt out and your data will no
longer be collected.
</p>
@@ -1,13 +1,14 @@
<script setup>
import { Button, Slider } from '@modrinth/ui'
import { ref, watch } from 'vue'
import { get, set } from '@/helpers/settings.ts'
import { purge_cache_types } from '@/helpers/cache.js'
import { handleError } from '@/store/notifications.js'
import { BoxIcon, FolderSearchIcon, TrashIcon } from '@modrinth/assets'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import { Button, injectNotificationManager, Slider } from '@modrinth/ui'
import { open } from '@tauri-apps/plugin-dialog'
import { ref, watch } from 'vue'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import { purge_cache_types } from '@/helpers/cache.js'
import { get, set } from '@/helpers/settings.ts'
const { handleError } = injectNotificationManager()
const settings = ref(await get())
watch(
@@ -100,37 +100,40 @@
</template>
<script setup lang="ts">
import { ref, computed, watch, useTemplateRef } from 'vue'
import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue'
import {
SkinPreviewRenderer,
CheckIcon,
ChevronRightIcon,
SaveIcon,
SpinnerIcon,
UploadIcon,
XIcon,
} from '@modrinth/assets'
import {
Button,
RadioButtons,
ButtonStyled,
CapeButton,
CapeLikeTextButton,
ButtonStyled,
injectNotificationManager,
RadioButtons,
SkinPreviewRenderer,
} from '@modrinth/ui'
import { computed, ref, useTemplateRef, watch } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue'
import UploadSkinModal from '@/components/ui/skin/UploadSkinModal.vue'
import {
add_and_equip_custom_skin,
remove_custom_skin,
unequip_skin,
type Skin,
type Cape,
type SkinModel,
get_normalized_skin_texture,
determineModelType,
get_normalized_skin_texture,
remove_custom_skin,
type Skin,
type SkinModel,
unequip_skin,
} from '@/helpers/skins.ts'
import { handleError } from '@/store/notifications'
import {
UploadIcon,
CheckIcon,
SaveIcon,
XIcon,
ChevronRightIcon,
SpinnerIcon,
} from '@modrinth/assets'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import UploadSkinModal from '@/components/ui/skin/UploadSkinModal.vue'
const { handleError } = injectNotificationManager()
const modal = useTemplateRef('modal')
const selectCapeModal = useTemplateRef('selectCapeModal')
@@ -1,15 +1,16 @@
<script setup lang="ts">
import { useTemplateRef, ref, computed } from 'vue'
import type { Cape, SkinModel } from '@/helpers/skins.ts'
import { CheckIcon, XIcon } from '@modrinth/assets'
import {
ButtonStyled,
ScrollablePanel,
CapeButton,
CapeLikeTextButton,
ScrollablePanel,
SkinPreviewRenderer,
} from '@modrinth/ui'
import { CheckIcon, XIcon } from '@modrinth/assets'
import { computed, ref, useTemplateRef } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import type { Cape, SkinModel } from '@/helpers/skins.ts'
const modal = useTemplateRef('modal')
@@ -27,14 +27,15 @@
</template>
<script setup lang="ts">
import { ref, onBeforeUnmount, watch } from 'vue'
import { UploadIcon } from '@modrinth/assets'
import { useNotifications } from '@/store/state'
import { injectNotificationManager } from '@modrinth/ui'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { onBeforeUnmount, ref, watch } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import { get_dragged_skin_data } from '@/helpers/skins'
const notifications = useNotifications()
const { addNotification } = injectNotificationManager()
const modal = ref()
const fileInput = ref<HTMLInputElement>()
@@ -99,7 +100,7 @@ async function setupDragDropListener() {
const data = await get_dragged_skin_data(filePath)
await processData(data.buffer)
} catch (error) {
notifications.addNotification({
addNotification({
title: 'Error processing file',
text: error instanceof Error ? error.message : 'Failed to read the dropped file.',
type: 'error',
@@ -1,6 +1,4 @@
<script setup lang="ts">
import type { Dayjs } from 'dayjs'
import dayjs from 'dayjs'
import {
EyeIcon,
FolderOpenIcon,
@@ -13,25 +11,29 @@ import {
Avatar,
ButtonStyled,
commonMessages,
injectNotificationManager,
OverflowMenu,
SmartClickable,
useRelativeTime,
} from '@modrinth/ui'
import { useVIntl } from '@vintl/vintl'
import { computed, nextTick, ref, onMounted, onUnmounted } from 'vue'
import { showProfileInFolder } from '@/helpers/utils'
import { convertFileSrc } from '@tauri-apps/api/core'
import { useRouter } from 'vue-router'
import type { GameInstance } from '@/helpers/types'
import { get_project } from '@/helpers/cache'
import { capitalizeString } from '@modrinth/utils'
import { kill, run } from '@/helpers/profile'
import { handleSevereError } from '@/store/error'
import { trackEvent } from '@/helpers/analytics'
import { get_by_profile_path } from '@/helpers/process'
import { handleError } from '@/store/notifications'
import { process_listener } from '@/helpers/events'
import { convertFileSrc } from '@tauri-apps/api/core'
import { useVIntl } from '@vintl/vintl'
import type { Dayjs } from 'dayjs'
import dayjs from 'dayjs'
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { trackEvent } from '@/helpers/analytics'
import { get_project } from '@/helpers/cache'
import { process_listener } from '@/helpers/events'
import { get_by_profile_path } from '@/helpers/process'
import { kill, run } from '@/helpers/profile'
import type { GameInstance } from '@/helpers/types'
import { showProfileInFolder } from '@/helpers/utils'
import { handleSevereError } from '@/store/error'
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const formatRelativeTime = useRelativeTime()
@@ -1,30 +1,32 @@
<script setup lang="ts">
import {
type ProtocolVersion,
type ServerWorld,
type ServerData,
type WorldWithProfile,
get_recent_worlds,
getWorldIdentifier,
get_profile_protocol_version,
refreshServerData,
start_join_server,
start_join_singleplayer_world,
} from '@/helpers/worlds.ts'
import { HeadingLink, GAME_MODES } from '@modrinth/ui'
import WorldItem from '@/components/ui/world/WorldItem.vue'
import InstanceItem from '@/components/ui/world/InstanceItem.vue'
import { watch, onMounted, onUnmounted, ref, computed } from 'vue'
import { GAME_MODES, HeadingLink, injectNotificationManager } from '@modrinth/ui'
import type { Dayjs } from 'dayjs'
import dayjs from 'dayjs'
import { useTheming } from '@/store/theme.ts'
import { kill, run } from '@/helpers/profile'
import { handleError } from '@/store/notifications'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import InstanceItem from '@/components/ui/world/InstanceItem.vue'
import WorldItem from '@/components/ui/world/WorldItem.vue'
import { trackEvent } from '@/helpers/analytics'
import { process_listener, profile_listener } from '@/helpers/events'
import { get_all } from '@/helpers/process'
import { kill, run } from '@/helpers/profile'
import type { GameInstance } from '@/helpers/types'
import {
get_profile_protocol_version,
get_recent_worlds,
getWorldIdentifier,
type ProtocolVersion,
refreshServerData,
type ServerData,
type ServerWorld,
start_join_server,
start_join_singleplayer_world,
type WorldWithProfile,
} from '@/helpers/worlds.ts'
import { handleSevereError } from '@/store/error'
import { useTheming } from '@/store/theme.ts'
const { handleError } = injectNotificationManager()
const props = defineProps<{
recentInstances: GameInstance[]
@@ -1,28 +1,10 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import type {
ProtocolVersion,
ServerStatus,
ServerWorld,
SingleplayerWorld,
World,
} from '@/helpers/worlds.ts'
import { set_world_display_status, getWorldIdentifier } from '@/helpers/worlds.ts'
import { formatNumber, getPingLevel } from '@modrinth/utils'
import {
useRelativeTime,
Avatar,
ButtonStyled,
commonMessages,
OverflowMenu,
SmartClickable,
} from '@modrinth/ui'
import {
IssuesIcon,
EyeIcon,
ClipboardCopyIcon,
EditIcon,
EyeIcon,
FolderOpenIcon,
IssuesIcon,
MoreVerticalIcon,
NoSignalIcon,
PlayIcon,
@@ -35,14 +17,33 @@ import {
UserIcon,
XIcon,
} from '@modrinth/assets'
import {
Avatar,
ButtonStyled,
commonMessages,
OverflowMenu,
SmartClickable,
useRelativeTime,
} from '@modrinth/ui'
import { formatNumber, getPingLevel } from '@modrinth/utils'
import { convertFileSrc } from '@tauri-apps/api/core'
import type { MessageDescriptor } from '@vintl/vintl'
import { defineMessages, useVIntl } from '@vintl/vintl'
import dayjs from 'dayjs'
import { Tooltip } from 'floating-vue'
import type { Component } from 'vue'
import { computed } from 'vue'
import { copyToClipboard } from '@/helpers/utils'
import { convertFileSrc } from '@tauri-apps/api/core'
import { useRouter } from 'vue-router'
import { Tooltip } from 'floating-vue'
import { copyToClipboard } from '@/helpers/utils'
import type {
ProtocolVersion,
ServerStatus,
ServerWorld,
SingleplayerWorld,
World,
} from '@/helpers/worlds.ts'
import { getWorldIdentifier, set_world_display_status } from '@/helpers/worlds.ts'
const { formatMessage } = useVIntl()
const formatRelativeTime = useRelativeTime()
@@ -235,7 +236,8 @@ const messages = defineMessages({
/>
<Tooltip :disabled="!hasPlayersTooltip">
<span :class="{ 'cursor-help': hasPlayersTooltip }">
{{ formatNumber(serverStatus.players?.online, false) }} online
{{ formatNumber(serverStatus.players?.online, false) }}
online
</span>
<template #popper>
<div class="flex flex-col gap-1">
@@ -248,7 +250,8 @@ const messages = defineMessages({
</template>
</template>
<template v-else>
<NoSignalIcon aria-hidden="true" stroke-width="3px" class="shrink-0" /> Offline
<NoSignalIcon aria-hidden="true" stroke-width="3px" class="shrink-0" />
Offline
</template>
</div>
</div>
@@ -258,7 +261,9 @@ const messages = defineMessages({
world.last_played ? dayjs(world.last_played).format('MMMM D, YYYY [at] h:mm A') : null
"
class="w-fit shrink-0"
:class="{ 'cursor-help smart-clickable:allow-pointer-events': world.last_played }"
:class="{
'cursor-help smart-clickable:allow-pointer-events': world.last_played,
}"
>
<template v-if="world.last_played">
{{
@@ -435,17 +440,20 @@ const messages = defineMessages({
{{ formatMessage(messages.viewInstance) }}
</template>
<template #edit>
<EditIcon aria-hidden="true" /> {{ formatMessage(commonMessages.editButton) }}
<EditIcon aria-hidden="true" />
{{ formatMessage(commonMessages.editButton) }}
</template>
<template #open-folder>
<FolderOpenIcon aria-hidden="true" />
{{ formatMessage(commonMessages.openFolderButton) }}
</template>
<template #copy-address>
<ClipboardCopyIcon aria-hidden="true" /> {{ formatMessage(messages.copyAddress) }}
<ClipboardCopyIcon aria-hidden="true" />
{{ formatMessage(messages.copyAddress) }}
</template>
<template #refresh>
<UpdatedIcon aria-hidden="true" /> {{ formatMessage(commonMessages.refreshButton) }}
<UpdatedIcon aria-hidden="true" />
{{ formatMessage(commonMessages.refreshButton) }}
</template>
<template #dont-show-on-home>
<XIcon aria-hidden="true" />
@@ -1,15 +1,16 @@
<script setup lang="ts">
import { PlayIcon, PlusIcon, XIcon } from '@modrinth/assets'
import { ButtonStyled, commonMessages } from '@modrinth/ui'
import { ref } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import type { GameInstance } from '@/helpers/types'
import InstanceModalTitlePrefix from '@/components/ui/modal/InstanceModalTitlePrefix.vue'
import { add_server_to_profile, type ServerPackStatus, type ServerWorld } from '@/helpers/worlds.ts'
import { ButtonStyled, commonMessages, injectNotificationManager } from '@modrinth/ui'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { handleError } from '@/store/notifications'
import ServerModalBody from '@/components/ui/world/modal/ServerModalBody.vue'
import { ref } from 'vue'
import InstanceModalTitlePrefix from '@/components/ui/modal/InstanceModalTitlePrefix.vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import ServerModalBody from '@/components/ui/world/modal/ServerModalBody.vue'
import type { GameInstance } from '@/helpers/types'
import { add_server_to_profile, type ServerPackStatus, type ServerWorld } from '@/helpers/worlds.ts'
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const emit = defineEmits<{
@@ -1,21 +1,22 @@
<script setup lang="ts">
import { SaveIcon, XIcon } from '@modrinth/assets'
import { ButtonStyled, commonMessages } from '@modrinth/ui'
import { ButtonStyled, commonMessages, injectNotificationManager } from '@modrinth/ui'
import { defineMessage, useVIntl } from '@vintl/vintl'
import { computed, ref } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import HideFromHomeOption from '@/components/ui/world/modal/HideFromHomeOption.vue'
import ServerModalBody from '@/components/ui/world/modal/ServerModalBody.vue'
import type { GameInstance } from '@/helpers/types'
import {
type ServerPackStatus,
type DisplayStatus,
edit_server_in_profile,
type ServerPackStatus,
type ServerWorld,
set_world_display_status,
type DisplayStatus,
} from '@/helpers/worlds.ts'
import { defineMessage, useVIntl } from '@vintl/vintl'
import { handleError } from '@/store/notifications'
import ServerModalBody from '@/components/ui/world/modal/ServerModalBody.vue'
import HideFromHomeOption from '@/components/ui/world/modal/HideFromHomeOption.vue'
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const emit = defineEmits<{
@@ -1,15 +1,16 @@
<script setup lang="ts">
import { ChevronRightIcon, SaveIcon, XIcon, UndoIcon } from '@modrinth/assets'
import { Avatar, ButtonStyled, commonMessages } from '@modrinth/ui'
import { ChevronRightIcon, SaveIcon, UndoIcon, XIcon } from '@modrinth/assets'
import { Avatar, ButtonStyled, commonMessages, injectNotificationManager } from '@modrinth/ui'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { computed, ref } from 'vue'
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import HideFromHomeOption from '@/components/ui/world/modal/HideFromHomeOption.vue'
import type { GameInstance } from '@/helpers/types'
import type { DisplayStatus, SingleplayerWorld } from '@/helpers/worlds.ts'
import { set_world_display_status, rename_world, reset_world_icon } from '@/helpers/worlds.ts'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { handleError } from '@/store/notifications'
import HideFromHomeOption from '@/components/ui/world/modal/HideFromHomeOption.vue'
import { rename_world, reset_world_icon, set_world_display_status } from '@/helpers/worlds.ts'
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const emit = defineEmits<{
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { Checkbox } from '@modrinth/ui'
import { defineMessage, useVIntl } from '@vintl/vintl'
import { computed } from 'vue'
import { Checkbox } from '@modrinth/ui'
const { formatMessage } = useVIntl()
const value = defineModel<boolean>({ required: true })
@@ -1,7 +1,8 @@
<script setup lang="ts">
import { TeleportDropdownMenu } from '@modrinth/ui'
import { defineMessages, type MessageDescriptor, useVIntl } from '@vintl/vintl'
import type { ServerPackStatus } from '@/helpers/worlds.ts'
import { type MessageDescriptor, defineMessages, useVIntl } from '@vintl/vintl'
const { formatMessage } = useVIntl()
@@ -1,4 +1,5 @@
import { invoke } from '@tauri-apps/api/core'
import cssContent from '@/assets/stylesheets/macFix.css?inline'
export async function useCheckDisableMouseover() {
@@ -1,9 +1,9 @@
import { ref, computed } from 'vue'
import { computed, ref } from 'vue'
import { get_max_memory } from '@/helpers/jre.js'
import { handleError } from '@/store/notifications.js'
export default async function () {
const maxMemory = ref(Math.floor((await get_max_memory().catch(handleError)) / 1024))
const maxMemory = ref(Math.floor((await get_max_memory()) / 1024))
const snapPoints = computed(() => {
let points = []
+4 -4
View File
@@ -1,6 +1,5 @@
import { fetch } from '@tauri-apps/plugin-http'
import { handleError } from '@/store/state.js'
import { getVersion } from '@tauri-apps/api/app'
import { fetch } from '@tauri-apps/plugin-http'
export const useFetch = async (url, item, isSilent) => {
try {
@@ -11,8 +10,9 @@ export const useFetch = async (url, item, isSilent) => {
})
} catch (err) {
if (!isSilent) {
handleError({ message: `Error fetching ${item}` })
}
throw err
} else {
console.error(err)
}
}
}
+1
View File
@@ -4,6 +4,7 @@
* and deserialized into a usable JS object.
*/
import { invoke } from '@tauri-apps/api/core'
import { create } from './profile'
/*
+5 -1
View File
@@ -28,7 +28,11 @@ export async function get_logs_by_filename(profilePath, logType, filename) {
/// Get a profile's log text only by filename
export async function get_output_by_filename(profilePath, logType, filename) {
return await invoke('plugin:logs|logs_get_output_by_filename', { profilePath, logType, filename })
return await invoke('plugin:logs|logs_get_output_by_filename', {
profilePath,
logType,
filename,
})
}
/// Delete a profile's log by filename
+1
View File
@@ -4,6 +4,7 @@
* and deserialized into a usable JS object.
*/
import { invoke } from '@tauri-apps/api/core'
import { create } from './profile'
// Installs pack from a version ID
+7 -4
View File
@@ -4,8 +4,8 @@
* and deserialized into a usable JS object.
*/
import { invoke } from '@tauri-apps/api/core'
import { install_to_existing_profile } from '@/helpers/pack.js'
import { handleError } from '@/store/notifications.js'
/// Add instance
/*
@@ -128,7 +128,10 @@ export async function remove_project(path, projectPath) {
// Update a managed Modrinth profile to a specific version
export async function update_managed_modrinth_version(path, versionId) {
return await invoke('plugin:profile|profile_update_managed_modrinth_version', { path, versionId })
return await invoke('plugin:profile|profile_update_managed_modrinth_version', {
path,
versionId,
})
}
// Repair a managed Modrinth profile
@@ -197,8 +200,8 @@ export async function finish_install(instance) {
linkedData.version_id,
instance.name,
instance.path,
).catch(handleError)
)
} else {
await install(instance.path, false).catch(handleError)
await install(instance.path, false)
}
}
@@ -1,17 +1,18 @@
import * as THREE from 'three'
import type { Skin, Cape } from '../skins'
import { get_normalized_skin_texture, determineModelType } from '../skins'
import { reactive } from 'vue'
import { ClassicPlayerModel, SlimPlayerModel } from '@modrinth/assets'
import {
setupSkinModel,
disposeCaches,
loadTexture,
applyCapeTexture,
createTransparentTexture,
disposeCaches,
loadTexture,
setupSkinModel,
} from '@modrinth/utils'
import { skinPreviewStorage } from '../storage/skin-preview-storage'
import * as THREE from 'three'
import { reactive } from 'vue'
import type { Cape, Skin } from '../skins'
import { determineModelType, get_normalized_skin_texture } from '../skins'
import { headStorage } from '../storage/head-storage'
import { ClassicPlayerModel, SlimPlayerModel } from '@modrinth/assets'
import { skinPreviewStorage } from '../storage/skin-preview-storage'
export interface RenderResult {
forwards: string
+2 -1
View File
@@ -4,8 +4,9 @@
* and deserialized into a usable JS object.
*/
import { invoke } from '@tauri-apps/api/core'
import type { ColorTheme, FeatureFlag } from '@/store/theme.ts'
import type { Hooks, MemorySettings, WindowSize } from '@/helpers/types'
import type { ColorTheme, FeatureFlag } from '@/store/theme.ts'
// Settings object
/*
+2 -3
View File
@@ -1,6 +1,5 @@
import { invoke } from '@tauri-apps/api/core'
import { handleError } from '@/store/notifications'
import { arrayBufferToBase64 } from '@modrinth/utils'
import { invoke } from '@tauri-apps/api/core'
export interface Cape {
id: string
@@ -39,7 +38,7 @@ export const DEFAULT_MODELS: Record<string, SkinModel> = {
export function filterSavedSkins(list: Skin[]) {
const customSkins = list.filter((s) => s.source !== 'default')
fixUnknownSkins(customSkins).catch(handleError)
fixUnknownSkins(customSkins)
return customSkins
}
+2 -1
View File
@@ -1,6 +1,7 @@
import { get_full_path, get_mod_full_path } from '@/helpers/profile'
import { invoke } from '@tauri-apps/api/core'
import { get_full_path, get_mod_full_path } from '@/helpers/profile'
export async function isDev() {
return await invoke('is_dev')
}
+4 -3
View File
@@ -1,9 +1,10 @@
import { autoToHTML } from '@geometrically/minecraft-motd-parser'
import type { GameVersion } from '@modrinth/ui'
import { invoke } from '@tauri-apps/api/core'
import dayjs from 'dayjs'
import { get_full_path } from '@/helpers/profile'
import { openPath } from '@/helpers/utils'
import { autoToHTML } from '@geometrically/minecraft-motd-parser'
import dayjs from 'dayjs'
import type { GameVersion } from '@modrinth/ui'
type BaseWorld = {
name: string
+8 -6
View File
@@ -1,12 +1,14 @@
import { createApp } from 'vue'
import router from '@/routes'
import App from '@/App.vue'
import { createPinia } from 'pinia'
import FloatingVue from 'floating-vue'
import 'floating-vue/dist/style.css'
import { createPlugin } from '@vintl/vintl/plugin'
import * as Sentry from '@sentry/vue'
import { VueScanPlugin } from '@taijased/vue-render-tracker'
import { createPlugin } from '@vintl/vintl/plugin'
import FloatingVue from 'floating-vue'
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from '@/App.vue'
import router from '@/routes'
const VIntlPlugin = createPlugin({
controllerOpts: {
+41 -15
View File
@@ -1,33 +1,35 @@
<script setup lang="ts">
import { computed, nextTick, ref, shallowRef, watch } from 'vue'
import type { Ref } from 'vue'
import { SearchIcon, XIcon, ClipboardCopyIcon, GlobeIcon, ExternalIcon } from '@modrinth/assets'
import { ClipboardCopyIcon, ExternalIcon, GlobeIcon, SearchIcon, XIcon } from '@modrinth/assets'
import type { Category, GameVersion, Platform, ProjectType, SortType, Tags } from '@modrinth/ui'
import {
SearchFilterControl,
SearchSidebarFilter,
Button,
Checkbox,
DropdownSelect,
injectNotificationManager,
LoadingIndicator,
Pagination,
SearchFilterControl,
SearchSidebarFilter,
useSearch,
} from '@modrinth/ui'
import { handleError } from '@/store/state'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
import { openUrl } from '@tauri-apps/plugin-opener'
import { defineMessages, useVIntl } from '@vintl/vintl'
import type { Ref } from 'vue'
import { computed, nextTick, ref, shallowRef, watch } from 'vue'
import type { LocationQuery } from 'vue-router'
import { useRoute, useRouter } from 'vue-router'
import SearchCard from '@/components/ui/SearchCard.vue'
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js'
import { get_search_results } from '@/helpers/cache.js'
import NavTabs from '@/components/ui/NavTabs.vue'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import type Instance from '@/components/ui/Instance.vue'
import InstanceIndicator from '@/components/ui/InstanceIndicator.vue'
import { defineMessages, useVIntl } from '@vintl/vintl'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import { openUrl } from '@tauri-apps/plugin-opener'
import NavTabs from '@/components/ui/NavTabs.vue'
import SearchCard from '@/components/ui/SearchCard.vue'
import { get_search_results } from '@/helpers/cache.js'
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile.js'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
import { useBreadcrumbs } from '@/store/breadcrumbs'
const { handleError } = injectNotificationManager()
const { formatMessage } = useVIntl()
const router = useRouter()
@@ -160,6 +162,8 @@ const {
createPageParams,
} = useSearch(projectTypes, tags, instanceFilters)
const previousFilterState = ref('')
const offline = ref(!navigator.onLine)
window.addEventListener('offline', () => {
offline.value = true
@@ -220,7 +224,20 @@ async function refreshSearch() {
}
}
results.value = rawResults.result
const currentFilterState = JSON.stringify({
query: query.value,
filters: currentFilters.value,
sort: currentSortType.value,
maxResults: maxResults.value,
projectTypes: projectTypes.value,
})
if (previousFilterState.value && previousFilterState.value !== currentFilterState) {
currentPage.value = 1
}
previousFilterState.value = currentFilterState
const persistentParams: LocationQuery = {}
@@ -380,6 +397,15 @@ const handleOptionsClick = (args) => {
}
await refreshSearch()
// Initialize previousFilterState after first search
previousFilterState.value = JSON.stringify({
query: query.value,
filters: currentFilters.value,
sort: currentSortType.value,
maxResults: maxResults.value,
projectTypes: projectTypes.value,
})
</script>
<template>
+13 -11
View File
@@ -1,17 +1,19 @@
<script setup lang="ts">
import { ref, onUnmounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import RowDisplay from '@/components/RowDisplay.vue'
import { list } from '@/helpers/profile.js'
import { profile_listener } from '@/helpers/events'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { handleError } from '@/store/notifications.js'
import dayjs from 'dayjs'
import { get_search_results } from '@/helpers/cache.js'
import { injectNotificationManager } from '@modrinth/ui'
import type { SearchResult } from '@modrinth/utils'
import RecentWorldsList from '@/components/ui/world/RecentWorldsList.vue'
import type { GameInstance } from '@/helpers/types'
import dayjs from 'dayjs'
import { computed, onUnmounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import RowDisplay from '@/components/RowDisplay.vue'
import RecentWorldsList from '@/components/ui/world/RecentWorldsList.vue'
import { get_search_results } from '@/helpers/cache.js'
import { profile_listener } from '@/helpers/events'
import { list } from '@/helpers/profile.js'
import type { GameInstance } from '@/helpers/types'
import { useBreadcrumbs } from '@/store/breadcrumbs'
const { handleError } = injectNotificationManager()
const route = useRoute()
const breadcrumbs = useBreadcrumbs()
+27 -18
View File
@@ -12,42 +12,44 @@ import {
Button,
ButtonStyled,
ConfirmModal,
injectNotificationManager,
SkinButton,
SkinLikeTextButton,
SkinPreviewRenderer,
} from '@modrinth/ui'
import { arrayBufferToBase64 } from '@modrinth/utils'
import { computedAsync } from '@vueuse/core'
import type { Ref } from 'vue'
import { computed, inject, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue'
import type AccountsCard from '@/components/ui/AccountsCard.vue'
import EditSkinModal from '@/components/ui/skin/EditSkinModal.vue'
import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue'
import UploadSkinModal from '@/components/ui/skin/UploadSkinModal.vue'
import { handleError, useNotifications } from '@/store/notifications'
import { trackEvent } from '@/helpers/analytics'
import { get_default_user, login as login_flow, users } from '@/helpers/auth'
import type { RenderResult } from '@/helpers/rendering/batch-skin-renderer.ts'
import { generateSkinPreviews, skinBlobUrlMap } from '@/helpers/rendering/batch-skin-renderer.ts'
import { get as getSettings } from '@/helpers/settings.ts'
import type { Cape, Skin } from '@/helpers/skins.ts'
import {
normalize_skin_texture,
equip_skin,
filterDefaultSkins,
filterSavedSkins,
get_available_capes,
get_available_skins,
get_normalized_skin_texture,
normalize_skin_texture,
remove_custom_skin,
set_default_cape,
} from '@/helpers/skins.ts'
import { get as getSettings } from '@/helpers/settings.ts'
import { get_default_user, login as login_flow, users } from '@/helpers/auth'
import type { RenderResult } from '@/helpers/rendering/batch-skin-renderer.ts'
import { generateSkinPreviews, skinBlobUrlMap } from '@/helpers/rendering/batch-skin-renderer.ts'
import { handleSevereError } from '@/store/error'
import { trackEvent } from '@/helpers/analytics'
import type AccountsCard from '@/components/ui/AccountsCard.vue'
import { arrayBufferToBase64 } from '@modrinth/utils'
const editSkinModal = useTemplateRef('editSkinModal')
const selectCapeModal = useTemplateRef('selectCapeModal')
const uploadSkinModal = useTemplateRef('uploadSkinModal')
const notifications = useNotifications()
const notifications = injectNotificationManager()
const { handleError } = notifications
const settings = ref(await getSettings())
const skins = ref<Skin[]>([])
@@ -64,7 +66,14 @@ const defaultCape = ref<Cape>()
const originalSelectedSkin = ref<Skin | null>(null)
const originalDefaultCape = ref<Cape>()
const savedSkins = computed(() => filterSavedSkins(skins.value))
const savedSkins = computed(() => {
try {
return filterSavedSkins(skins.value)
} catch (error) {
handleError(error as Error)
return []
}
})
const defaultSkins = computed(() => filterDefaultSkins(skins.value))
const currentCape = computed(() => {
@@ -113,7 +122,7 @@ async function loadCapes() {
defaultCape.value = capes.value.find((c) => c.is_equipped)
originalDefaultCape.value = defaultCape.value
} catch (error) {
if (currentUser.value) {
if (currentUser.value && error instanceof Error) {
handleError(error)
}
}
@@ -126,7 +135,7 @@ async function loadSkins() {
selectedSkin.value = skins.value.find((s) => s.is_equipped) ?? null
originalSelectedSkin.value = selectedSkin.value
} catch (error) {
if (currentUser.value) {
if (currentUser.value && error instanceof Error) {
handleError(error)
}
}
@@ -161,7 +170,7 @@ async function changeSkin(newSkin: Skin) {
text: "You're changing your skin too frequently. Mojang's servers have temporarily blocked further requests. Please wait a moment before trying again.",
})
} else {
handleError(error)
handleError(error as Error)
}
}
}
@@ -190,7 +199,7 @@ async function handleCapeSelected(cape: Cape | undefined) {
text: "You're changing your cape too frequently. Mojang's servers have temporarily blocked further requests. Please wait a moment before trying again.",
})
} else {
handleError(error)
handleError(error as Error)
}
}
}
@@ -207,7 +216,7 @@ async function loadCurrentUser() {
const allAccounts = await users()
currentUser.value = allAccounts.find((acc) => acc.profile.id === defaultId)
} catch (e) {
handleError(e)
handleError(e as Error)
currentUser.value = undefined
currentUserId.value = undefined
}
@@ -276,7 +285,7 @@ async function checkUserChanges() {
await loadSkins()
}
} catch (error) {
if (currentUser.value) {
if (currentUser.value && error instanceof Error) {
handleError(error)
}
}
@@ -376,7 +385,7 @@ await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
color="green"
aria-label="Edit skin"
class="pointer-events-auto"
@click.stop="(e) => editSkinModal?.show(e, skin)"
@click.stop="(e: MouseEvent) => editSkinModal?.show(e, skin)"
>
<EditIcon /> Edit
</Button>
+3 -3
View File
@@ -1,6 +1,6 @@
import Index from './Index.vue'
import Browse from './Browse.vue'
import Worlds from './Worlds.vue'
import Index from './Index.vue'
import Skins from './Skins.vue'
import Worlds from './Worlds.vue'
export { Index, Browse, Worlds, Skins }
export { Browse, Index, Skins, Worlds }
+23 -20
View File
@@ -157,13 +157,6 @@
</div>
</template>
<script setup>
import {
Avatar,
ButtonStyled,
ContentPageHeader,
LoadingIndicator,
OverflowMenu,
} from '@modrinth/ui'
import {
CheckCircleIcon,
ClipboardCopyIcon,
@@ -187,28 +180,38 @@ import {
UserPlusIcon,
XIcon,
} from '@modrinth/assets'
import { finish_install, get, get_full_path, kill, run } from '@/helpers/profile'
import { get_by_profile_path } from '@/helpers/process'
import { process_listener, profile_listener } from '@/helpers/events'
import { useRoute, useRouter } from 'vue-router'
import { computed, onUnmounted, ref, watch } from 'vue'
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
import { showProfileInFolder } from '@/helpers/utils.js'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import NavTabs from '@/components/ui/NavTabs.vue'
import { trackEvent } from '@/helpers/analytics'
import {
Avatar,
ButtonStyled,
ContentPageHeader,
injectNotificationManager,
LoadingIndicator,
OverflowMenu,
} from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import { handleSevereError } from '@/store/error.js'
import { get_project, get_version_many } from '@/helpers/cache.js'
import dayjs from 'dayjs'
import duration from 'dayjs/plugin/duration'
import relativeTime from 'dayjs/plugin/relativeTime'
import { computed, onUnmounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import ExportModal from '@/components/ui/ExportModal.vue'
import InstanceSettingsModal from '@/components/ui/modal/InstanceSettingsModal.vue'
import NavTabs from '@/components/ui/NavTabs.vue'
import { trackEvent } from '@/helpers/analytics'
import { get_project, get_version_many } from '@/helpers/cache.js'
import { process_listener, profile_listener } from '@/helpers/events'
import { get_by_profile_path } from '@/helpers/process'
import { finish_install, get, get_full_path, kill, run } from '@/helpers/profile'
import { showProfileInFolder } from '@/helpers/utils.js'
import { handleSevereError } from '@/store/error.js'
import { useBreadcrumbs, useLoading } from '@/store/state'
dayjs.extend(duration)
dayjs.extend(relativeTime)
const { handleError } = injectNotificationManager()
const route = useRoute()
const router = useRouter()
@@ -328,7 +331,7 @@ const stopInstance = async (context) => {
}
const repairInstance = async () => {
await finish_install(instance.value)
await finish_install(instance.value).catch(handleError)
}
const handleRightClick = (event) => {
+15 -13
View File
@@ -88,30 +88,32 @@
</template>
<script setup>
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import { CheckIcon, ClipboardCopyIcon, ShareIcon, TrashIcon } from '@modrinth/assets'
import { Button, Card, Checkbox, DropdownSelect } from '@modrinth/ui'
import {
delete_logs_by_filename,
get_logs,
get_output_by_filename,
get_latest_log_cursor,
} from '@/helpers/logs.js'
import { computed, nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, watch } from 'vue'
import { Button, Card, Checkbox, DropdownSelect, injectNotificationManager } from '@modrinth/ui'
import dayjs from 'dayjs'
import isToday from 'dayjs/plugin/isToday'
import isYesterday from 'dayjs/plugin/isYesterday'
import { get_by_profile_path } from '@/helpers/process.js'
import { useRoute } from 'vue-router'
import { process_listener } from '@/helpers/events.js'
import { handleError } from '@/store/notifications.js'
import { ofetch } from 'ofetch'
import { computed, nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
import { process_listener } from '@/helpers/events.js'
import {
delete_logs_by_filename,
get_latest_log_cursor,
get_logs,
get_output_by_filename,
} from '@/helpers/logs.js'
import { get_by_profile_path } from '@/helpers/process.js'
dayjs.extend(isToday)
dayjs.extend(isYesterday)
const { handleError } = injectNotificationManager()
const route = useRoute()
const props = defineProps({
+27 -21
View File
@@ -60,7 +60,10 @@
if (x.id) {
item.project = {
id: x.id,
link: { path: `/project/${x.id}`, query: { i: props.instance.path } },
link: {
path: `/project/${x.id}`,
query: { i: props.instance.path },
},
linkProps: {},
}
}
@@ -271,16 +274,35 @@ import {
Button,
ButtonStyled,
ContentListPanel,
injectNotificationManager,
OverflowMenu,
Pagination,
RadialHeader,
Toggle,
} from '@modrinth/ui'
import type { ContentItem } from '@modrinth/ui/src/components/content/ContentListItem.vue'
import type { Organization, Project, TeamMember, Version } from '@modrinth/utils'
import { formatProjectType } from '@modrinth/utils'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { defineMessages, useVIntl } from '@vintl/vintl'
import dayjs from 'dayjs'
import type { ComputedRef } from 'vue'
import { computed, onUnmounted, ref, watch } from 'vue'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { TextInputIcon } from '@/assets/icons'
import AddContentButton from '@/components/ui/AddContentButton.vue'
import type ContextMenu from '@/components/ui/ContextMenu.vue'
import ExportModal from '@/components/ui/ExportModal.vue'
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
import { trackEvent } from '@/helpers/analytics'
import {
get_organization_many,
get_project_many,
get_team_many,
get_version_many,
} from '@/helpers/cache.js'
import { profile_listener } from '@/helpers/events.js'
import {
add_project_from_path,
get_projects,
@@ -289,26 +311,10 @@ import {
update_all,
update_project,
} from '@/helpers/profile.js'
import { handleError } from '@/store/notifications.js'
import { trackEvent } from '@/helpers/analytics'
import { highlightModInProfile } from '@/helpers/utils.js'
import { TextInputIcon } from '@/assets/icons'
import ExportModal from '@/components/ui/ExportModal.vue'
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
import AddContentButton from '@/components/ui/AddContentButton.vue'
import {
get_organization_many,
get_project_many,
get_team_many,
get_version_many,
} from '@/helpers/cache.js'
import { profile_listener } from '@/helpers/events.js'
import ShareModalWrapper from '@/components/ui/modal/ShareModalWrapper.vue'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import dayjs from 'dayjs'
import type { CacheBehaviour, ContentFile, GameInstance } from '@/helpers/types'
import type ContextMenu from '@/components/ui/ContextMenu.vue'
import type { ContentItem } from '@modrinth/ui/src/components/content/ContentListItem.vue'
import { highlightModInProfile } from '@/helpers/utils.js'
const { handleError } = injectNotificationManager()
const props = defineProps<{
instance: GameInstance
@@ -1,9 +1,10 @@
<template>{{ instance.name }} overview</template>
<script setup lang="ts">
import type { GameInstance } from '@/helpers/types'
import type ContextMenu from '@/components/ui/ContextMenu.vue'
import type { Version } from '@modrinth/utils'
import type ContextMenu from '@/components/ui/ContextMenu.vue'
import type { GameInstance } from '@/helpers/types'
defineProps<{
instance: GameInstance
options: InstanceType<typeof ContextMenu>
+41 -40
View File
@@ -121,55 +121,56 @@
</div>
</template>
<script setup lang="ts">
import { ref, computed, onUnmounted, watch } from 'vue'
import { useRoute } from 'vue-router'
import type { GameInstance } from '@/helpers/types'
import { PlusIcon, SearchIcon, SpinnerIcon, UpdatedIcon, XIcon } from '@modrinth/assets'
import {
Button,
ButtonStyled,
RadialHeader,
FilterBar,
type FilterBarOption,
type GameVersion,
GAME_MODES,
type GameVersion,
injectNotificationManager,
RadialHeader,
} from '@modrinth/ui'
import { PlusIcon, SpinnerIcon, UpdatedIcon, SearchIcon, XIcon } from '@modrinth/assets'
import {
type ProtocolVersion,
type SingleplayerWorld,
type World,
type ServerWorld,
type ServerData,
type ProfileEvent,
get_profile_protocol_version,
remove_server_from_profile,
delete_world,
start_join_server,
start_join_singleplayer_world,
getWorldIdentifier,
refreshServerData,
refreshWorld,
sortWorlds,
refreshServers,
hasWorldQuickPlaySupport,
refreshWorlds,
handleDefaultProfileUpdateEvent,
showWorldInFolder,
hasServerQuickPlaySupport,
} from '@/helpers/worlds.ts'
import type { Version } from '@modrinth/utils'
import { defineMessages } from '@vintl/vintl'
import { computed, onUnmounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import type ContextMenu from '@/components/ui/ContextMenu.vue'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import AddServerModal from '@/components/ui/world/modal/AddServerModal.vue'
import EditServerModal from '@/components/ui/world/modal/EditServerModal.vue'
import EditWorldModal from '@/components/ui/world/modal/EditSingleplayerWorldModal.vue'
import WorldItem from '@/components/ui/world/WorldItem.vue'
import ConfirmModalWrapper from '@/components/ui/modal/ConfirmModalWrapper.vue'
import { handleError } from '@/store/notifications'
import type ContextMenu from '@/components/ui/ContextMenu.vue'
import type { Version } from '@modrinth/utils'
import { profile_listener } from '@/helpers/events'
import { get_game_versions } from '@/helpers/tags'
import { defineMessages } from '@vintl/vintl'
import type { GameInstance } from '@/helpers/types'
import {
delete_world,
get_profile_protocol_version,
getWorldIdentifier,
handleDefaultProfileUpdateEvent,
hasServerQuickPlaySupport,
hasWorldQuickPlaySupport,
type ProfileEvent,
type ProtocolVersion,
refreshServerData,
refreshServers,
refreshWorld,
refreshWorlds,
remove_server_from_profile,
type ServerData,
type ServerWorld,
showWorldInFolder,
type SingleplayerWorld,
sortWorlds,
start_join_server,
start_join_singleplayer_world,
type World,
} from '@/helpers/worlds.ts'
const { handleError } = injectNotificationManager()
const route = useRoute()
const addServerModal = ref<InstanceType<typeof AddServerModal>>()
@@ -282,7 +283,7 @@ async function editServer(server: ServerWorld) {
await refreshServer(server.address)
}
} else {
handleError(`Error refreshing server, refreshing all worlds`)
handleError(new Error(`Error refreshing server, refreshing all worlds`))
await refreshAllWorlds()
}
}
@@ -301,7 +302,7 @@ async function editWorld(path: string, name: string, removeIcon: boolean) {
}
sortWorlds(worlds.value)
} else {
handleError(`Error finding world in list, refreshing all worlds`)
handleError(new Error(`Error finding world in list, refreshing all worlds`))
await refreshAllWorlds()
}
}
@@ -311,7 +312,7 @@ async function deleteWorld(world: SingleplayerWorld) {
worlds.value = worlds.value.filter((w) => w.type !== 'singleplayer' || w.path !== world.path)
}
function handleJoinError(err: unknown) {
function handleJoinError(err: Error) {
handleError(err)
startingInstance.value = false
worldPlaying.value = undefined
@@ -436,7 +437,7 @@ function promptToRemoveWorld(world: World): boolean {
async function proceedRemoveServer() {
if (!serverToRemove.value) {
handleError(`Error removing server, no server marked for removal.`)
handleError(new Error(`Error removing server, no server marked for removal.`))
return
}
await removeServer(serverToRemove.value)
@@ -445,7 +446,7 @@ async function proceedRemoveServer() {
async function proceedDeleteWorld() {
if (!worldToDelete.value) {
handleError(`Error deleting world, no world marked for removal.`)
handleError(new Error(`Error deleting world, no world marked for removal.`))
return
}
await deleteWorld(worldToDelete.value)
@@ -1,7 +1,7 @@
import Index from './Index.vue'
import Logs from './Logs.vue'
import Mods from './Mods.vue'
import Overview from './Overview.vue'
import Worlds from './Worlds.vue'
import Mods from './Mods.vue'
import Logs from './Logs.vue'
export { Index, Overview, Worlds, Mods, Logs }
export { Index, Logs, Mods, Overview, Worlds }
+11 -10
View File
@@ -1,16 +1,17 @@
<script setup>
import { onUnmounted, ref, shallowRef } from 'vue'
import { list } from '@/helpers/profile.js'
import { useRoute } from 'vue-router'
import { useBreadcrumbs } from '@/store/breadcrumbs.js'
import { profile_listener } from '@/helpers/events.js'
import { handleError } from '@/store/notifications.js'
import { Button } from '@modrinth/ui'
import { PlusIcon } from '@modrinth/assets'
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
import { NewInstanceImage } from '@/assets/icons'
import NavTabs from '@/components/ui/NavTabs.vue'
import { Button, injectNotificationManager } from '@modrinth/ui'
import { onUnmounted, ref, shallowRef } from 'vue'
import { useRoute } from 'vue-router'
import { NewInstanceImage } from '@/assets/icons'
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
import NavTabs from '@/components/ui/NavTabs.vue'
import { profile_listener } from '@/helpers/events.js'
import { list } from '@/helpers/profile.js'
import { useBreadcrumbs } from '@/store/breadcrumbs.js'
const { handleError } = injectNotificationManager()
const route = useRoute()
const breadcrumbs = useBreadcrumbs()
+3 -3
View File
@@ -1,6 +1,6 @@
import Custom from './Custom.vue'
import Downloaded from './Downloaded.vue'
import Index from './Index.vue'
import Overview from './Overview.vue'
import Downloaded from './Downloaded.vue'
import Custom from './Custom.vue'
export { Index, Overview, Downloaded, Custom }
export { Custom, Downloaded, Index, Overview }
@@ -83,16 +83,18 @@
<script setup>
import {
ExpandIcon,
RightArrowIcon,
LeftArrowIcon,
ExternalIcon,
ContractIcon,
XIcon,
CalendarIcon,
ContractIcon,
ExpandIcon,
ExternalIcon,
LeftArrowIcon,
RightArrowIcon,
XIcon,
} from '@modrinth/assets'
import { Button, Card } from '@modrinth/ui'
import { ref } from 'vue'
// import { hide_ads_window, show_ads_window } from '@/helpers/ads.js'
import { trackEvent } from '@/helpers/analytics'
const props = defineProps({
@@ -108,6 +110,7 @@ const zoomedIn = ref(false)
const hideImage = () => {
expandedGalleryItem.value = null
// show_ads_window()
}
const nextImage = () => {
@@ -135,6 +138,7 @@ const previousImage = () => {
}
const expandImage = (item, index) => {
// hide_ads_window()
expandedGalleryItem.value = item
expandedGalleryIndex.value = index
zoomedIn.value = false
+23 -22
View File
@@ -131,44 +131,45 @@
<script setup>
import {
BookmarkIcon,
MoreVerticalIcon,
DownloadIcon,
ReportIcon,
HeartIcon,
ExternalIcon,
CheckIcon,
GlobeIcon,
ClipboardCopyIcon,
DownloadIcon,
ExternalIcon,
GlobeIcon,
HeartIcon,
MoreVerticalIcon,
ReportIcon,
} from '@modrinth/assets'
import {
ButtonStyled,
injectNotificationManager,
OverflowMenu,
ProjectBackgroundGradient,
ProjectHeader,
ProjectSidebarCompatibility,
ButtonStyled,
OverflowMenu,
ProjectSidebarLinks,
ProjectSidebarCreators,
ProjectSidebarDetails,
ProjectBackgroundGradient,
ProjectSidebarLinks,
} from '@modrinth/ui'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile'
import { openUrl } from '@tauri-apps/plugin-opener'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { useRoute, useRouter } from 'vue-router'
import { ref, shallowRef, watch } from 'vue'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { handleError } from '@/store/notifications.js'
import { useRoute, useRouter } from 'vue-router'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import { install as installVersion } from '@/store/install.js'
import { get_project, get_team, get_version_many } from '@/helpers/cache.js'
import NavTabs from '@/components/ui/NavTabs.vue'
import { useTheming } from '@/store/state.js'
import InstanceIndicator from '@/components/ui/InstanceIndicator.vue'
import { openUrl } from '@tauri-apps/plugin-opener'
import NavTabs from '@/components/ui/NavTabs.vue'
import { get_project, get_team, get_version_many } from '@/helpers/cache.js'
import { get as getInstance, get_projects as getInstanceProjects } from '@/helpers/profile'
import { get_categories, get_game_versions, get_loaders } from '@/helpers/tags'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { install as installVersion } from '@/store/install.js'
import { useTheming } from '@/store/state.js'
dayjs.extend(relativeTime)
const { handleError } = injectNotificationManager()
const route = useRoute()
const router = useRouter()
const breadcrumbs = useBreadcrumbs()
@@ -251,7 +252,7 @@ async function install(version) {
(profile) => {
router.push(`/instance/${profile}`)
},
)
).catch(handleError)
}
const options = ref(null)
@@ -182,15 +182,16 @@
</template>
<script setup>
import { DownloadIcon, FileIcon, ReportIcon, ExternalIcon, CheckIcon } from '@modrinth/assets'
import { CheckIcon, DownloadIcon, ExternalIcon, FileIcon, ReportIcon } from '@modrinth/assets'
import { Avatar, Badge, Breadcrumbs, Button, Card, CopyCode } from '@modrinth/ui'
import { formatBytes, renderString } from '@modrinth/utils'
import { Breadcrumbs, Badge, Avatar, Card, Button, CopyCode } from '@modrinth/ui'
import { releaseColor } from '@/helpers/utils'
import { ref, watch, computed } from 'vue'
import { computed, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { SwapIcon } from '@/assets/icons'
import { get_project_many, get_version_many } from '@/helpers/cache.js'
import { releaseColor } from '@/helpers/utils'
import { useBreadcrumbs } from '@/store/breadcrumbs'
const breadcrumbs = useBreadcrumbs()
@@ -65,12 +65,17 @@
</template>
<script setup>
import { ProjectPageVersions, ButtonStyled, OverflowMenu } from '@modrinth/ui'
import { CheckIcon, DownloadIcon, ExternalIcon, MoreVerticalIcon } from '@modrinth/assets'
import {
ButtonStyled,
injectNotificationManager,
OverflowMenu,
ProjectPageVersions,
} from '@modrinth/ui'
import { ref } from 'vue'
import { SwapIcon } from '@/assets/icons/index.js'
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
import { handleError } from '@/store/notifications.js'
defineProps({
project: {
@@ -103,6 +108,8 @@ defineProps({
},
})
const { handleError } = injectNotificationManager()
const [loaders, gameVersions] = await Promise.all([
get_loaders().catch(handleError).then(ref),
get_game_versions().catch(handleError).then(ref),
+3 -3
View File
@@ -1,7 +1,7 @@
import Index from './Index.vue'
import Description from './Description.vue'
import Versions from './Versions.vue'
import Gallery from './Gallery.vue'
import Index from './Index.vue'
import Version from './Version.vue'
import Versions from './Versions.vue'
export { Index, Description, Versions, Gallery, Version }
export { Description, Gallery, Index, Version, Versions }
@@ -0,0 +1,48 @@
import {
AbstractWebNotificationManager,
type NotificationPanelLocation,
type WebNotification,
} from '@modrinth/ui'
import { type Ref, ref } from 'vue'
export class AppNotificationManager extends AbstractWebNotificationManager {
private readonly state: Ref<WebNotification[]>
private readonly locationState: Ref<NotificationPanelLocation>
public constructor() {
super()
this.state = ref<WebNotification[]>([])
this.locationState = ref<NotificationPanelLocation>('right')
}
public getNotificationLocation(): NotificationPanelLocation {
return this.locationState.value
}
public setNotificationLocation(location: NotificationPanelLocation): void {
this.locationState.value = location
}
public getNotifications(): WebNotification[] {
return this.state.value
}
protected addNotificationToStorage(notification: WebNotification): void {
this.state.value.push(notification)
}
protected removeNotificationFromStorage(id: string | number): void {
const index = this.state.value.findIndex((n) => n.id === id)
if (index > -1) {
this.state.value.splice(index, 1)
}
}
protected removeNotificationFromStorageByIndex(index: number): void {
this.state.value.splice(index, 1)
}
protected clearAllNotificationsFromStorage(): void {
this.state.value.splice(0)
}
}
+2 -1
View File
@@ -1,8 +1,9 @@
import { createRouter, createWebHistory } from 'vue-router'
import * as Pages from '@/pages'
import * as Project from '@/pages/project'
import * as Instance from '@/pages/instance'
import * as Library from '@/pages/library'
import * as Project from '@/pages/project'
/**
* Configures application routing. Add page to pages/index and then add to route table here.
+20 -28
View File
@@ -1,4 +1,9 @@
import dayjs from 'dayjs'
import { defineStore } from 'pinia'
import { trackEvent } from '@/helpers/analytics.js'
import { get_project, get_version_many } from '@/helpers/cache.js'
import { create_profile_and_install as packInstall } from '@/helpers/pack.js'
import {
add_project_from_version,
check_installed,
@@ -7,11 +12,6 @@ import {
list,
remove_project,
} from '@/helpers/profile.js'
import { handleError } from '@/store/notifications.js'
import { get_project, get_version_many } from '@/helpers/cache.js'
import { create_profile_and_install as packInstall } from '@/helpers/pack.js'
import { trackEvent } from '@/helpers/analytics.js'
import dayjs from 'dayjs'
export const useInstall = defineStore('installStore', {
state: () => ({
@@ -49,11 +49,11 @@ export const install = async (
callback = () => {},
createInstanceCallback = () => {},
) => {
const project = await get_project(projectId, 'must_revalidate').catch(handleError)
const project = await get_project(projectId, 'must_revalidate')
if (project.project_type === 'modpack') {
const version = versionId ?? project.versions[project.versions.length - 1]
const packs = await list().catch(handleError)
const packs = await list()
if (packs.length === 0 || !packs.find((pack) => pack.linked_data?.project_id === project.id)) {
await packInstall(
@@ -62,7 +62,7 @@ export const install = async (
project.title,
project.icon_url,
createInstanceCallback,
).catch(handleError)
)
trackEvent('PackInstall', {
id: project.id,
@@ -79,8 +79,8 @@ export const install = async (
} else {
if (instancePath) {
const [instance, instanceProjects, versions] = await Promise.all([
await get(instancePath).catch(handleError),
await get_projects(instancePath).catch(handleError),
await get(instancePath),
await get_projects(instancePath),
await get_version_many(project.versions, 'must_revalidate'),
])
@@ -117,7 +117,7 @@ export const install = async (
}
}
await add_project_from_version(instance.path, version.id).catch(handleError)
await add_project_from_version(instance.path, version.id)
await installVersionDependencies(instance, version)
trackEvent('ProjectInstall', {
@@ -142,7 +142,7 @@ export const install = async (
)
}
} else {
const versions = (await get_version_many(project.versions).catch(handleError)).sort(
const versions = (await get_version_many(project.versions)).sort(
(a, b) => dayjs(b.date_published) - dayjs(a.date_published),
)
@@ -171,30 +171,22 @@ export const installVersionDependencies = async (profile, version) => {
// disallow fabric api install on quilt
if (dep.project_id === 'P7dR8mSH' && profile.loader === 'quilt') continue
if (dep.version_id) {
if (
dep.project_id &&
(await check_installed(profile.path, dep.project_id).catch(handleError))
)
continue
if (dep.project_id && (await check_installed(profile.path, dep.project_id))) continue
await add_project_from_version(profile.path, dep.version_id)
} else {
if (
dep.project_id &&
(await check_installed(profile.path, dep.project_id).catch(handleError))
if (dep.project_id && (await check_installed(profile.path, dep.project_id))) continue
const depProject = await get_project(dep.project_id, 'must_revalidate')
const depVersions = (await get_version_many(depProject.versions, 'must_revalidate')).sort(
(a, b) => dayjs(b.date_published) - dayjs(a.date_published),
)
continue
const depProject = await get_project(dep.project_id, 'must_revalidate').catch(handleError)
const depVersions = (
await get_version_many(depProject.versions, 'must_revalidate').catch(handleError)
).sort((a, b) => dayjs(b.date_published) - dayjs(a.date_published))
const latest = depVersions.find(
(v) => v.game_versions.includes(profile.game_version) && v.loaders.includes(profile.loader),
)
if (latest) {
await add_project_from_version(profile.path, latest.id).catch(handleError)
await add_project_from_version(profile.path, latest.id)
}
}
}

Some files were not shown because too many files have changed in this diff Show More