diff --git a/CLAUDE.md b/CLAUDE.md index 7cb1dad1b6..ffd69006c4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -67,10 +67,22 @@ Each project may have its own `CLAUDE.md` with detailed instructions: - [`apps/labrinth/CLAUDE.md`](apps/labrinth/CLAUDE.md) — Backend API - [`apps/frontend/CLAUDE.md`](apps/frontend/CLAUDE.md) - Frontend Website +## Skills (`.claude/skills/`) + +Project-specific skill files with detailed patterns. Use them when the task matches: + +- **`api-module`** — Adding a new API endpoint module to `packages/api-client` (types, module class, registry registration) +- **`cross-platform-pages`** — Building a page that needs to work in both the website (`apps/frontend`) and the desktop app (`apps/app-frontend`) +- **`dependency-injection`** — Creating or wiring up a `provide`/`inject` context for platform abstraction or deep component state sharing +- **`figma-mcp`** — Translating a Figma design into Vue components using the Figma MCP tools +- **`i18n-convert`** — Converting hardcoded English strings in Vue SFCs into the `@modrinth/ui` i18n system (`defineMessages`, `formatMessage`, `IntlFormatted`) +- **`multistage-modals`** — Building a wizard-like modal with multiple stages, progress tracking, and per-stage buttons using `MultiStageModal` +- **`tanstack-query`** — Fetching, caching, or mutating server data with `@tanstack/vue-query` (queries, mutations, invalidation, optimistic updates) + ## Code Guidelines ### Comments -- DO NOT use "heading" comments like: // === Helper methods === . +- DO NOT use "heading" comments like: `=== Helper methods ===`. - Use doc comments, but avoid inline comments unless ABSOLUTELY necessary for clarity. Code should aim to be self documenting! ## Bash Guidelines @@ -78,12 +90,14 @@ Each project may have its own `CLAUDE.md` with detailed instructions: ### Output handling - DO NOT pipe output through `head`, `tail`, `less`, or `more` - NEVER use `| head -n X` or `| tail -n X` to truncate output -- Run commands directly without pipes when possible -- If you need to limit output, use command-specific flags (e.g. `git log -n 10` instead of `git log | head -10`) +- IMPORTANT: Run commands directly without pipes when possible +- IMPORTANT: If you need to limit output, use command-specific flags (e.g. `git log -n 10` instead of `git log | head -10`) - ALWAYS read the full output — never pipe through filters ### General - Do not create new non-source code files (e.g. Bash scripts, SQL scripts) unless explicitly prompted to +- For Frontend, when doing lint checks, only use the `prepr` commands, do not use `typecheck` or `tsc` etc. +- When editing, if the file has tabs USE TABS in edit tool - do not go back and forth identifying tabs in the file. ## Skills diff --git a/apps/app-frontend/package.json b/apps/app-frontend/package.json index c7561a588b..d892df69f6 100644 --- a/apps/app-frontend/package.json +++ b/apps/app-frontend/package.json @@ -22,23 +22,24 @@ "@tanstack/vue-query": "^5.90.7", "@tauri-apps/api": "^2.5.0", "@tauri-apps/plugin-dialog": "^2.2.1", - "@tauri-apps/plugin-http": "^2.5.0", + "@tauri-apps/plugin-http": "~2.5.7", "@tauri-apps/plugin-opener": "^2.2.6", "@tauri-apps/plugin-os": "^2.2.1", "@tauri-apps/plugin-updater": "^2.7.1", "@tauri-apps/plugin-window-state": "^2.2.2", "@types/three": "^0.172.0", - "intl-messageformat": "^10.7.7", - "vue-i18n": "^10.0.0", "@vueuse/core": "^11.1.0", "dayjs": "^1.11.10", "floating-vue": "^5.2.2", + "fuse.js": "^6.6.2", + "intl-messageformat": "^10.7.7", "ofetch": "^1.3.4", "pinia": "^3.0.0", "posthog-js": "^1.158.2", "three": "^0.172.0", "vite-svg-loader": "^5.1.0", "vue": "^3.5.13", + "vue-i18n": "^10.0.0", "vue-multiselect": "3.0.0", "vue-router": "^4.6.0", "vue-virtual-scroller": "v2.0.0-beta.8" diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index eed52f8356..f7a029b5c3 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -31,6 +31,8 @@ import { Button, ButtonStyled, commonMessages, + ContentInstallModal, + CreationFlowModal, defineMessages, I18nDebugPanel, NewsArticleCard, @@ -38,6 +40,7 @@ import { OverflowMenu, PopupNotificationPanel, ProgressSpinner, + provideModalBehavior, provideModrinthClient, provideNotificationManager, providePageContext, @@ -66,8 +69,6 @@ import FriendsList from '@/components/ui/friends/FriendsList.vue' import AddServerToInstanceModal from '@/components/ui/install_flow/AddServerToInstanceModal.vue' import IncompatibilityWarningModal from '@/components/ui/install_flow/IncompatibilityWarningModal.vue' import InstallConfirmModal from '@/components/ui/install_flow/InstallConfirmModal.vue' -import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue' -import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue' import MinecraftAuthErrorModal from '@/components/ui/minecraft-auth-error-modal/MinecraftAuthErrorModal.vue' import AppSettingsModal from '@/components/ui/modal/AppSettingsModal.vue' import AuthGrantFlowWaitModal from '@/components/ui/modal/AuthGrantFlowWaitModal.vue' @@ -86,6 +87,7 @@ import { check_reachable } from '@/helpers/auth.js' import { get_user } from '@/helpers/cache.js' import { command_listener, warning_listener } from '@/helpers/events.js' import { cancelLogin, get as getCreds, login, logout } from '@/helpers/mr_auth.ts' +import { create_profile_and_install_from_file } from '@/helpers/pack' import { list } from '@/helpers/profile.js' import { get as getSettings, set as setSettings } from '@/helpers/settings.ts' import { get_opening_command, initialize_state } from '@/helpers/state' @@ -98,15 +100,16 @@ import { isNetworkMetered, } from '@/helpers/utils.js' import i18n from '@/i18n.config' +import { createContentInstall, provideContentInstall } from '@/providers/content-install' import { provideAppUpdateDownloadProgress, subscribeToDownloadProgress, } from '@/providers/download-progress.ts' +import { createServerInstall, provideServerInstall } from '@/providers/server-install' +import { setupProviders } from '@/providers/setup' import { useError } from '@/store/error.js' -import { playServerProject, useInstall } from '@/store/install.js' import { useLoading, useTheming } from '@/store/state' -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' @@ -136,6 +139,20 @@ providePageContext({ hierarchicalSidebarAvailable: ref(true), showAds: ref(false), }) +provideModalBehavior({ + noblur: computed(() => !themeStore.advancedRendering), + onShow: () => hide_ads_window(), + onHide: () => show_ads_window(), +}) + +const { + installationModal, + handleCreate, + handleBrowseModpacks, + searchModpacks, + getProjectVersions, +} = setupProviders(notificationManager) + const news = ref([]) const availableSurvey = ref(false) @@ -392,7 +409,34 @@ const error = useError() const errorModal = ref() const minecraftAuthErrorModal = ref() -const install = useInstall() +const contentInstall = createContentInstall({ router, handleError }) +provideContentInstall(contentInstall) +const { + instances: contentInstallInstances, + compatibleLoaders: contentInstallLoaders, + gameVersions: contentInstallGameVersions, + loading: contentInstallLoading, + defaultTab: contentInstallDefaultTab, + preferredLoader: contentInstallPreferredLoader, + preferredGameVersion: contentInstallPreferredGameVersion, + releaseGameVersions: contentInstallReleaseGameVersions, + handleInstallToInstance, + handleCreateAndInstall, + handleNavigate: handleContentInstallNavigate, + handleCancel: handleContentInstallCancel, + setContentInstallModal, + setInstallConfirmModal: setContentInstallConfirmModal, + setIncompatibilityWarningModal: setContentIncompatibilityWarningModal, +} = contentInstall + +const serverInstall = createServerInstall({ router, handleError, popupNotificationManager }) +provideServerInstall(serverInstall) +const { + setInstallToPlayModal: setServerInstallToPlayModal, + setUpdateToPlayModal: setServerUpdateToPlayModal, + setAddServerToInstanceModal: setServerAddServerToInstanceModal, +} = serverInstall + const modInstallModal = ref() const addServerToInstanceModal = ref() const installConfirmModal = ref() @@ -474,13 +518,12 @@ onMounted(() => { error.setErrorModal(errorModal.value) error.setMinecraftAuthErrorModal(minecraftAuthErrorModal.value) - install.setIncompatibilityWarningModal(incompatibilityWarningModal) - install.setInstallConfirmModal(installConfirmModal) - install.setModInstallModal(modInstallModal) - install.setAddServerToInstanceModal(addServerToInstanceModal) - install.setInstallToPlayModal(installToPlayModal) - install.setUpdateToPlayModal(updateToPlayModal) - install.setPopupNotificationManager(popupNotificationManager) + setContentIncompatibilityWarningModal(incompatibilityWarningModal.value) + setContentInstallConfirmModal(installConfirmModal.value) + setContentInstallModal(modInstallModal.value) + setServerAddServerToInstanceModal(addServerToInstanceModal.value) + setServerInstallToPlayModal(installToPlayModal.value) + setServerUpdateToPlayModal(updateToPlayModal.value) }) const accounts = ref(null) @@ -898,9 +941,15 @@ provideAppUpdateDownloadProgress(appUpdateDownload) - - - +
@@ -946,7 +995,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload) @@ -1021,9 +1070,9 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
-
- -
+
+ +
-
+
- + diff --git a/apps/app-frontend/src/components/RowDisplay.vue b/apps/app-frontend/src/components/RowDisplay.vue index a8e8970aed..56c65fa0b4 100644 --- a/apps/app-frontend/src/components/RowDisplay.vue +++ b/apps/app-frontend/src/components/RowDisplay.vue @@ -24,10 +24,11 @@ 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 { injectContentInstall } from '@/providers/content-install' import { handleSevereError } from '@/store/error.js' -import { install as installVersion } from '@/store/install.js' const { handleError } = injectNotificationManager() +const { install: installVersion } = injectContentInstall() const router = useRouter() diff --git a/apps/app-frontend/src/components/ui/Breadcrumbs.vue b/apps/app-frontend/src/components/ui/Breadcrumbs.vue index ee12e17ea7..144ed5ebc0 100644 --- a/apps/app-frontend/src/components/ui/Breadcrumbs.vue +++ b/apps/app-frontend/src/components/ui/Breadcrumbs.vue @@ -1,64 +1,147 @@ - + + diff --git a/apps/app-frontend/src/components/ui/ExportModal.vue b/apps/app-frontend/src/components/ui/ExportModal.vue index 4966b1c7bd..fa605f3206 100644 --- a/apps/app-frontend/src/components/ui/ExportModal.vue +++ b/apps/app-frontend/src/components/ui/ExportModal.vue @@ -1,6 +1,14 @@ @@ -377,7 +369,6 @@ import { ChevronLeftIcon, CompassIcon, EditIcon, - EmptyIllustration, GlobeIcon, HeartMinusIcon, LinkIcon, @@ -398,6 +389,7 @@ import { ConfirmModal, defineMessage, defineMessages, + EmptyState, FileInput, HorizontalRule, injectModrinthClient, diff --git a/apps/frontend/src/pages/dashboard/revenue/transfers.vue b/apps/frontend/src/pages/dashboard/revenue/transfers.vue index ecabbf92f2..e1a65450f2 100644 --- a/apps/frontend/src/pages/dashboard/revenue/transfers.vue +++ b/apps/frontend/src/pages/dashboard/revenue/transfers.vue @@ -72,14 +72,11 @@
-
- {{ - formatMessage(messages.noTransactions) - }} - {{ - formatMessage(messages.noTransactionsDesc) - }} -
+