mirror of
https://github.com/modrinth/code.git
synced 2026-07-31 13:16:38 +00:00
Compare commits
30
Commits
4989bffa9a
...
9859e8310e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9859e8310e | ||
|
|
501757b9d9 | ||
|
|
e1774217c6 | ||
|
|
bc1c7c80c6 | ||
|
|
23933c1f49 | ||
|
|
da52062fab | ||
|
|
bca1e511ec | ||
|
|
bdc2a60f9b | ||
|
|
96d65902d7 | ||
|
|
6e0fb0d192 | ||
|
|
aad7386023 | ||
|
|
f7ed9e74e0 | ||
|
|
1f1ecc728a | ||
|
|
3ad76ba505 | ||
|
|
78ca6364d7 | ||
|
|
31cadd1f23 | ||
|
|
65a3ac4b34 | ||
|
|
295db3fb7a | ||
|
|
7abe6f8c73 | ||
|
|
c5ce5bc9b3 | ||
|
|
572b8027ff | ||
|
|
0a53211389 | ||
|
|
22b2b7746e | ||
|
|
39b3e20d0a | ||
|
|
3892b62451 | ||
|
|
5ffffec2e5 | ||
|
|
a0724cc5fb | ||
|
|
a0320f425e | ||
|
|
06262e7bb1 | ||
|
|
9ccaab5fad |
@@ -92,7 +92,6 @@ jobs:
|
||||
run: corepack enable
|
||||
|
||||
- name: Set up caches
|
||||
if: contains(matrix.artifact-target-name, 'linux') || contains(matrix.artifact-target-name, 'darwin')
|
||||
uses: namespacelabs/nscloud-cache-action@c5f8dab7560444c4bf8dbc64f1b203431873c547 # v1.6.1
|
||||
with:
|
||||
cache: |
|
||||
@@ -100,7 +99,6 @@ jobs:
|
||||
pnpm
|
||||
|
||||
- name: Configure sccache
|
||||
if: contains(matrix.artifact-target-name, 'linux') || contains(matrix.artifact-target-name, 'darwin')
|
||||
run: nsc cache sccache setup --cache_name default >> "$GITHUB_ENV"
|
||||
|
||||
- name: Generate tauri-dev.conf.json
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup>
|
||||
import { ButtonLink, IconButton, TeleportOverflowMenu } from '@modrinth/ui'
|
||||
import {
|
||||
AuthFeature,
|
||||
ModrinthApiError,
|
||||
@@ -26,33 +27,7 @@ import {
|
||||
ShirtIcon,
|
||||
UserIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
ContentInstallModal,
|
||||
ContentUpdaterModal,
|
||||
CreationFlowModal,
|
||||
defineMessages,
|
||||
I18nDebugPanel,
|
||||
IntlFormatted,
|
||||
LoadingBar,
|
||||
NewsArticleCard,
|
||||
NotificationPanel,
|
||||
OverflowMenu,
|
||||
PopupNotificationPanel,
|
||||
provideModalBehavior,
|
||||
provideModrinthClient,
|
||||
provideNotificationManager,
|
||||
providePageContext,
|
||||
providePopupNotificationManager,
|
||||
TextLogo,
|
||||
useDebugLogger,
|
||||
useFormatBytes,
|
||||
useHostingIntercom,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { Admonition, Avatar, commonMessages, ContentInstallModal, ContentUpdaterModal, CreationFlowModal, defineMessages, I18nDebugPanel, IntlFormatted, LoadingBar, NewsArticleCard, NotificationPanel, PopupNotificationPanel, provideModalBehavior, provideModrinthClient, provideNotificationManager, providePageContext, providePopupNotificationManager, TextLogo, useDebugLogger, useFormatBytes, useHostingIntercom, useVIntl } from '@modrinth/ui'
|
||||
import { renderString } from '@modrinth/utils'
|
||||
import { useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { getVersion } from '@tauri-apps/api/app'
|
||||
@@ -1591,22 +1566,25 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
|
||||
>
|
||||
<SettingsIcon />
|
||||
</NavButton>
|
||||
<OverflowMenu
|
||||
<TeleportOverflowMenu label="More options"
|
||||
v-if="credentials?.user"
|
||||
v-tooltip.right="formatMessage(messages.modrinthAccount)"
|
||||
class="w-12 h-12 text-primary rounded-full flex items-center justify-center text-2xl transition-all bg-transparent hover:bg-button-bg hover:text-contrast border-0 cursor-pointer"
|
||||
:options="[
|
||||
{
|
||||
id: 'view-profile',
|
||||
label: formatMessage(messages.signedInAs, {
|
||||
username: credentials.user.username,
|
||||
}),
|
||||
action: () => router.push(`/user/${encodeURIComponent(credentials.user.username)}`),
|
||||
},
|
||||
{
|
||||
id: 'sign-out',
|
||||
label: formatMessage(commonMessages.signOutButton),
|
||||
action: () => logOut(),
|
||||
color: 'danger',
|
||||
},
|
||||
]"
|
||||
placement="right-end"
|
||||
placement="bottom-start"
|
||||
>
|
||||
<Avatar :src="credentials?.user?.avatar_url" alt="" size="32px" circle />
|
||||
<template #view-profile>
|
||||
@@ -1629,7 +1607,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
|
||||
<LogOutIcon />
|
||||
{{ formatMessage(commonMessages.signOutButton) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</TeleportOverflowMenu>
|
||||
<NavButton
|
||||
v-else
|
||||
v-tooltip.right="formatMessage(messages.signInToModrinthAccount)"
|
||||
@@ -1642,49 +1620,37 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
|
||||
<div data-tauri-drag-region class="flex min-w-0 flex-1 items-center overflow-hidden p-2">
|
||||
<TextLogo class="h-7 w-auto shrink-0 text-contrast pointer-events-none" />
|
||||
<div data-tauri-drag-region class="ml-2 flex shrink-0 items-center gap-2">
|
||||
<ButtonStyled type="outlined" circular>
|
||||
<button
|
||||
<IconButton type="outlined" label="Go back"
|
||||
class="!h-7 !min-w-7 !w-7 !border !border-surface-4 !p-0 !opacity-100"
|
||||
:disabled="!canNavigateBack"
|
||||
aria-label="Go back"
|
||||
@click="router.back()"
|
||||
>
|
||||
<ChevronLeftIcon
|
||||
class="!size-4 !text-primary"
|
||||
:class="{ 'opacity-20': !canNavigateBack }"
|
||||
/>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled type="outlined" circular>
|
||||
<button
|
||||
<ChevronLeftIcon
|
||||
class="!size-4 !text-primary"
|
||||
:class="{ 'opacity-20': !canNavigateBack }"
|
||||
/>
|
||||
</IconButton>
|
||||
<IconButton type="outlined" label="Go forward"
|
||||
class="!h-7 !min-w-7 !w-7 !border !border-surface-4 !p-0 !opacity-100"
|
||||
:disabled="!canNavigateForward"
|
||||
aria-label="Go forward"
|
||||
@click="router.forward()"
|
||||
>
|
||||
<ChevronRightIcon
|
||||
class="!size-4 !text-primary"
|
||||
:class="{ 'opacity-20': !canNavigateForward }"
|
||||
/>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ChevronRightIcon
|
||||
class="!size-4 !text-primary"
|
||||
:class="{ 'opacity-20': !canNavigateForward }"
|
||||
/>
|
||||
</IconButton>
|
||||
</div>
|
||||
<Breadcrumbs />
|
||||
</div>
|
||||
<section data-tauri-drag-region class="flex shrink-0 ml-auto items-center">
|
||||
<ButtonStyled
|
||||
v-if="!forceSidebar && themeStore.toggleSidebar"
|
||||
:type="sidebarToggled ? 'standard' : 'transparent'"
|
||||
circular
|
||||
>
|
||||
<button
|
||||
<IconButton :type="sidebarToggled ? 'base' : 'quiet'" label="Next image" v-if="!forceSidebar && themeStore.toggleSidebar"
|
||||
class="mr-3 transition-transform"
|
||||
:class="{ 'rotate-180': !sidebarToggled }"
|
||||
@click="sidebarToggled = !sidebarToggled"
|
||||
>
|
||||
<RightArrowIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<RightArrowIcon />
|
||||
</IconButton>
|
||||
<div class="flex mr-3">
|
||||
<Suspense>
|
||||
<AppActionBar />
|
||||
@@ -1793,12 +1759,10 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
|
||||
:key="`news-${index}`"
|
||||
:article="item"
|
||||
/>
|
||||
<ButtonStyled color="brand" size="large">
|
||||
<a href="https://modrinth.com/news" target="_blank" class="my-4">
|
||||
<NewspaperIcon />
|
||||
{{ formatMessage(messages.viewAllNews) }}
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonLink type="colored" color="brand" size="xl" href="https://modrinth.com/news" target="_blank" class="my-4">
|
||||
<NewspaperIcon />
|
||||
{{ formatMessage(messages.viewAllNews) }}
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,13 +4,11 @@
|
||||
class="flex flex-col gap-3 bg-button-bg border border-solid border-surface-5 rounded-xl p-3 mt-2"
|
||||
>
|
||||
<span>{{ formatMessage(messages.notSignedIn) }}</span>
|
||||
<ButtonStyled color="brand">
|
||||
<button color="primary" :disabled="loginDisabled" @click="login()">
|
||||
<LogInIcon v-if="!loginDisabled" />
|
||||
<SpinnerIcon v-else class="animate-spin" />
|
||||
{{ formatMessage(messages.signInToMinecraft) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" :disabled="loginDisabled" @click="login()">
|
||||
<LogInIcon v-if="!loginDisabled" />
|
||||
<SpinnerIcon v-else class="animate-spin" />
|
||||
{{ formatMessage(messages.signInToMinecraft) }}
|
||||
</Button>
|
||||
</div>
|
||||
<Accordion
|
||||
v-else
|
||||
@@ -60,30 +58,27 @@
|
||||
{{ account.profile.name }}
|
||||
</p>
|
||||
</button>
|
||||
<ButtonStyled circular color="red" color-fill="none" hover-color-fill="background">
|
||||
<button
|
||||
<IconButton type="quiet" color="red" :label="formatMessage(messages.removeAccount)"
|
||||
v-tooltip="formatMessage(messages.removeAccount)"
|
||||
class="mr-2"
|
||||
class="mr-2 hover:!bg-red focus-visible:!bg-red hover:!text-[var(--color-accent-contrast)] focus-visible:!text-[var(--color-accent-contrast)]"
|
||||
@click="logout(account.profile.id)"
|
||||
>
|
||||
<TrashIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<TrashIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex flex-col gap-2 px-2 pt-2">
|
||||
<ButtonStyled v-if="accounts.length > 0" class="w-full">
|
||||
<button :disabled="loginDisabled" @click="login()">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.addAccount) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button v-if="accounts.length > 0" class="w-full" :disabled="loginDisabled" @click="login()">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.addAccount) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Accordion>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, IconButton } from '@modrinth/ui'
|
||||
import {
|
||||
LogInIcon,
|
||||
PlusIcon,
|
||||
@@ -95,7 +90,6 @@ import {
|
||||
import {
|
||||
Accordion,
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
useVIntl,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Button, TeleportOverflowMenu } from '@modrinth/ui'
|
||||
import { DropdownIcon, FolderOpenIcon, PlusIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, injectNotificationManager, OverflowMenu } from '@modrinth/ui'
|
||||
import { injectNotificationManager } from '@modrinth/ui'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
@@ -36,27 +37,24 @@ const handleSearchContent = async () => {
|
||||
|
||||
<template>
|
||||
<div class="joined-buttons">
|
||||
<ButtonStyled>
|
||||
<button @click="handleSearchContent">
|
||||
<PlusIcon />
|
||||
Install content
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<OverflowMenu
|
||||
<Button @click="handleSearchContent">
|
||||
<PlusIcon />
|
||||
Install content
|
||||
</Button>
|
||||
<TeleportOverflowMenu label="More options"
|
||||
:options="[
|
||||
{
|
||||
id: 'from_file',
|
||||
label: 'Add from file',
|
||||
action: handleAddContentFromFile,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<DropdownIcon />
|
||||
<template #from_file>
|
||||
<FolderOpenIcon />
|
||||
<span class="no-wrap"> Add from file </span>
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
class="!w-auto !px-2.5 !rounded-xl">
|
||||
<DropdownIcon />
|
||||
<template #from_file>
|
||||
<FolderOpenIcon />
|
||||
<span class="no-wrap"> Add from file </span>
|
||||
</template>
|
||||
</TeleportOverflowMenu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
<template>
|
||||
<div class="flex gap-2 items-center">
|
||||
<ButtonStyled
|
||||
v-if="hasActiveLoadingBars && !hasVisibleActiveDownloadToasts"
|
||||
color="brand"
|
||||
type="transparent"
|
||||
circular
|
||||
>
|
||||
<button v-tooltip="formatMessage(messages.viewActiveDownloads)" @click="openDownloadToast()">
|
||||
<DownloadIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<IconButton type="quiet" color="brand" :label="formatMessage(messages.viewActiveDownloads)" v-if="hasActiveLoadingBars && !hasVisibleActiveDownloadToasts" v-tooltip="formatMessage(messages.viewActiveDownloads)" @click="openDownloadToast()">
|
||||
<DownloadIcon />
|
||||
</IconButton>
|
||||
<div v-if="offline" class="flex items-center gap-1">
|
||||
<UnplugIcon class="text-secondary" />
|
||||
<span class="text-sm text-contrast"> {{ formatMessage(messages.offline) }} </span>
|
||||
@@ -36,17 +29,19 @@
|
||||
@show="showInstances = true"
|
||||
@hide="showInstances = false"
|
||||
>
|
||||
<ButtonStyled type="transparent" circular size="small">
|
||||
<button
|
||||
<IconButton class="!size-6" type="quiet" size="xs" :label="
|
||||
showInstances
|
||||
? formatMessage(messages.hideMoreRunningInstances)
|
||||
: formatMessage(messages.showMoreRunningInstances)
|
||||
"
|
||||
v-tooltip="
|
||||
showInstances
|
||||
? formatMessage(messages.hideMoreRunningInstances)
|
||||
: formatMessage(messages.showMoreRunningInstances)
|
||||
"
|
||||
>
|
||||
<DropdownIcon :class="{ 'rotate-180': !!showInstances }" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<DropdownIcon :class="{ 'rotate-180': !!showInstances }" />
|
||||
</IconButton>
|
||||
<template #popper>
|
||||
<div class="flex w-[20rem] max-h-[24rem] flex-col gap-2 overflow-auto">
|
||||
<div
|
||||
@@ -116,6 +111,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { IconButton } from '@modrinth/ui'
|
||||
import {
|
||||
DownloadIcon,
|
||||
DropdownIcon,
|
||||
@@ -126,7 +122,6 @@ import {
|
||||
UnplugIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
injectPopupNotificationManager,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup>
|
||||
import { Button, ButtonLink, IconButton } from '@modrinth/ui'
|
||||
import {
|
||||
CheckIcon,
|
||||
CopyIcon,
|
||||
@@ -9,7 +10,7 @@ import {
|
||||
WrenchIcon,
|
||||
XIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { ButtonStyled, Collapsible, injectNotificationManager } from '@modrinth/ui'
|
||||
import { Collapsible, injectNotificationManager } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import { ChatIcon } from '@/assets/icons'
|
||||
@@ -273,12 +274,8 @@ async function copyToClipboard(text) {
|
||||
</template>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<ButtonStyled>
|
||||
<a :href="supportLink" @click="errorModal.hide()"><ChatIcon /> Get support</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="closable">
|
||||
<button @click="errorModal.hide()"><XIcon /> Close</button>
|
||||
</ButtonStyled>
|
||||
<ButtonLink :href="supportLink" @click="errorModal.hide()"><ChatIcon /> Get support</ButtonLink>
|
||||
<Button v-if="closable" @click="errorModal.hide()"><XIcon /> Close</Button>
|
||||
</div>
|
||||
<template v-if="hasDebugInfo">
|
||||
<div class="flex flex-col gap-2">
|
||||
@@ -307,16 +304,14 @@ async function copyToClipboard(text) {
|
||||
>
|
||||
{{ debugInfo }}
|
||||
</div>
|
||||
<ButtonStyled circular>
|
||||
<button
|
||||
<IconButton :label="'Copy debug info'"
|
||||
v-tooltip="'Copy debug info'"
|
||||
:disabled="copied"
|
||||
@click="copyToClipboard(debugInfo)"
|
||||
>
|
||||
<template v-if="copied"> <CheckIcon class="text-green" /> </template>
|
||||
<template v-else> <CopyIcon /> </template>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<template v-if="copied"> <CheckIcon class="text-green" /> </template>
|
||||
<template v-else> <CopyIcon /> </template>
|
||||
</IconButton>
|
||||
</div>
|
||||
</Collapsible>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
FileTreeSelect,
|
||||
@@ -286,18 +286,14 @@ function isExportCandidateDisabled(path) {
|
||||
</div>
|
||||
<template #actions>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="exportModal.hide">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="exportPack">
|
||||
<PackageIcon />
|
||||
{{ formatMessage(messages.exportButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" @click="exportModal.hide">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" @click="exportPack">
|
||||
<PackageIcon />
|
||||
{{ formatMessage(messages.exportButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup>
|
||||
import { IconButton } from '@modrinth/ui'
|
||||
import {
|
||||
DownloadIcon,
|
||||
GameIcon,
|
||||
@@ -7,7 +8,7 @@ import {
|
||||
StopCircleIcon,
|
||||
TimerIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { Avatar, ButtonStyled, injectNotificationManager, useRelativeTime } from '@modrinth/ui'
|
||||
import { Avatar, injectNotificationManager, useRelativeTime } from '@modrinth/ui'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
@@ -168,30 +169,24 @@ onUnmounted(() => unlisten())
|
||||
<span class="line-clamp-2">{{ instance.name }}</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<ButtonStyled v-if="playing" color="red" circular @mousehover="checkProcess">
|
||||
<button v-tooltip="'Stop'" @click="(e) => stop(e, 'InstanceCard')">
|
||||
<StopCircleIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else-if="modLoading" color="standard" circular>
|
||||
<button v-tooltip="'Instance is loading...'" disabled>
|
||||
<SpinnerIcon class="animate-spin" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled
|
||||
<IconButton type="colored" color="red" :label="'Stop'" v-if="playing" @mouseenter="checkProcess" v-tooltip="'Stop'" @click="(e) => stop(e, 'InstanceCard')">
|
||||
<StopCircleIcon />
|
||||
</IconButton>
|
||||
<IconButton :label="'Instance is loading...'" v-else-if="modLoading" v-tooltip="'Instance is loading...'" disabled>
|
||||
<SpinnerIcon class="animate-spin" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
v-else-if="!instance.quarantined"
|
||||
:color="first ? 'brand' : 'standard'"
|
||||
circular
|
||||
>
|
||||
<button
|
||||
:type="first ? 'colored' : 'base'"
|
||||
:color="first ? 'brand' : undefined"
|
||||
label="Play"
|
||||
v-tooltip="'Play'"
|
||||
@click="(e) => play(e, 'InstanceCard')"
|
||||
@mousehover="checkProcess"
|
||||
@mouseenter="checkProcess"
|
||||
>
|
||||
<!-- Translate for optical centering -->
|
||||
<PlayIcon class="translate-x-[1px]" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<!-- Translate for optical centering -->
|
||||
<PlayIcon class="translate-x-[1px]" />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div class="flex items-center col-span-3 gap-1 text-secondary font-semibold">
|
||||
<TimerIcon />
|
||||
@@ -219,47 +214,36 @@ onUnmounted(() => unlisten())
|
||||
:class="`transition-all ${modLoading || installing ? `brightness-[0.25] scale-[0.85]` : `group-hover:brightness-75`}`"
|
||||
/>
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<ButtonStyled v-if="playing" size="large" color="red" circular>
|
||||
<button
|
||||
<IconButton type="colored" color="red" size="xl" :label="'Stop'" v-if="playing"
|
||||
v-tooltip="'Stop'"
|
||||
:class="{ 'scale-100 opacity-100': playing }"
|
||||
class="transition-all scale-75 origin-bottom opacity-0 card-shadow"
|
||||
@click="(e) => stop(e, 'InstanceCard')"
|
||||
@mousehover="checkProcess"
|
||||
@mouseenter="checkProcess"
|
||||
>
|
||||
<StopCircleIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<StopCircleIcon />
|
||||
</IconButton>
|
||||
<SpinnerIcon
|
||||
v-else-if="modLoading || installing"
|
||||
v-tooltip="modLoading ? 'Instance is loading...' : 'Installing...'"
|
||||
class="animate-spin w-8 h-8"
|
||||
tabindex="-1"
|
||||
/>
|
||||
<ButtonStyled
|
||||
v-else-if="!installed && !instance.quarantined"
|
||||
size="large"
|
||||
color="brand"
|
||||
circular
|
||||
>
|
||||
<button
|
||||
<IconButton type="colored" color="brand" size="xl" :label="'Repair'" v-else-if="!installed && !instance.quarantined"
|
||||
v-tooltip="'Repair'"
|
||||
class="transition-all scale-75 group-hover:scale-100 group-focus-within:scale-100 origin-bottom opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 card-shadow"
|
||||
@click="(e) => repair(e)"
|
||||
>
|
||||
<DownloadIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else-if="!instance.quarantined" size="large" color="brand" circular>
|
||||
<button
|
||||
<DownloadIcon />
|
||||
</IconButton>
|
||||
<IconButton type="colored" color="brand" size="xl" :label="'Play'" v-else-if="!instance.quarantined"
|
||||
v-tooltip="'Play'"
|
||||
class="transition-all scale-75 group-hover:scale-100 group-focus-within:scale-100 origin-bottom opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 card-shadow"
|
||||
@click="(e) => play(e, 'InstanceCard')"
|
||||
@mousehover="checkProcess"
|
||||
@mouseenter="checkProcess"
|
||||
>
|
||||
<PlayIcon class="translate-x-[2px]" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<PlayIcon class="translate-x-[2px]" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ButtonLink } from '@modrinth/ui'
|
||||
import { GameIcon, LeftArrowIcon } from '@modrinth/assets'
|
||||
import { Avatar, ButtonStyled, FormattedTag } from '@modrinth/ui'
|
||||
import { Avatar, FormattedTag } from '@modrinth/ui'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { computed } from 'vue'
|
||||
|
||||
@@ -48,9 +49,7 @@ const instanceLink = computed(() => {
|
||||
</span>
|
||||
</span>
|
||||
</router-link>
|
||||
<ButtonStyled>
|
||||
<router-link :to="instanceLink"> <LeftArrowIcon /> Back to instance </router-link>
|
||||
</ButtonStyled>
|
||||
<ButtonLink :to="instanceLink"> <LeftArrowIcon /> Back to instance </ButtonLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -14,18 +14,14 @@
|
||||
</template>
|
||||
<template #cell-actions="{ row }">
|
||||
<div class="flex items-center justify-end">
|
||||
<ButtonStyled v-if="currentSelected.path === row.path">
|
||||
<button class="!shadow-none" disabled>
|
||||
<CheckIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.selected) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else>
|
||||
<button class="!shadow-none" @click="setJavaInstall(row)">
|
||||
<PlusIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.select) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button v-if="currentSelected.path === row.path" disabled>
|
||||
<CheckIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.selected) }}
|
||||
</Button>
|
||||
<Button v-else @click="setJavaInstall(row)">
|
||||
<PlusIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.select) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
<template #empty-state>
|
||||
@@ -35,23 +31,21 @@
|
||||
</template>
|
||||
</Table>
|
||||
<div class="flex justify-end">
|
||||
<ButtonStyled type="outlined">
|
||||
<button
|
||||
class="!shadow-none !border-surface-4 !border"
|
||||
<Button type="outlined"
|
||||
class="!border-surface-4 !border"
|
||||
@click="$refs.detectJavaModal.hide()"
|
||||
>
|
||||
<XIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.cancel) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<XIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.cancel) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalWrapper>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { CheckIcon, PlusIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
Table,
|
||||
|
||||
@@ -17,69 +17,78 @@
|
||||
}
|
||||
"
|
||||
/>
|
||||
<ButtonStyled
|
||||
<Button
|
||||
type="quiet"
|
||||
:color="
|
||||
!hoveringTest && !testingJava
|
||||
? testingJavaSuccess === true
|
||||
? 'green'
|
||||
: 'red'
|
||||
: 'standard'
|
||||
: undefined
|
||||
"
|
||||
color-fill="text"
|
||||
>
|
||||
<button
|
||||
:aria-label="formatMessage(messages.testJavaInstallation)"
|
||||
class="!shadow-none"
|
||||
class="!text-[var(--legacy-button-color,var(--color-base))] [&>svg]:!text-[var(--legacy-button-color,var(--color-primary))]"
|
||||
:disabled="testingJava || props.disabled"
|
||||
@click="runTest(props.modelValue?.path)"
|
||||
@mouseenter="!props.disabled && (hoveringTest = true)"
|
||||
@mouseleave="hoveringTest = false"
|
||||
>
|
||||
<SpinnerIcon v-if="testingJava" class="animate-spin h-4 w-4" />
|
||||
<CheckCircleIcon
|
||||
v-else-if="testingJavaSuccess === true && !hoveringTest"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
<XCircleIcon v-else-if="testingJavaSuccess !== true && !hoveringTest" class="h-4 w-4" />
|
||||
<RefreshCwIcon v-else-if="!props.disabled" class="h-4 w-4" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
:style="{ '--legacy-button-color': (
|
||||
!hoveringTest && !testingJava
|
||||
? testingJavaSuccess === true
|
||||
? 'green'
|
||||
: 'red'
|
||||
: 'standard'
|
||||
) && (
|
||||
!hoveringTest && !testingJava
|
||||
? testingJavaSuccess === true
|
||||
? 'green'
|
||||
: 'red'
|
||||
: 'standard'
|
||||
) !== 'standard' ? `var(--color-${
|
||||
!hoveringTest && !testingJava
|
||||
? testingJavaSuccess === true
|
||||
? 'green'
|
||||
: 'red'
|
||||
: 'standard'
|
||||
})` : undefined }">
|
||||
<SpinnerIcon v-if="testingJava" class="animate-spin h-4 w-4" />
|
||||
<CheckCircleIcon
|
||||
v-else-if="testingJavaSuccess === true && !hoveringTest"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
<XCircleIcon v-else-if="testingJavaSuccess !== true && !hoveringTest" class="h-4 w-4" />
|
||||
<RefreshCwIcon v-else-if="!props.disabled" class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<span class="installation-buttons">
|
||||
<ButtonStyled v-if="props.version">
|
||||
<button
|
||||
<Button v-if="props.version"
|
||||
v-tooltip="
|
||||
testingJavaSuccess === true ? formatMessage(messages.alreadyInstalled) : undefined
|
||||
"
|
||||
class="!shadow-none"
|
||||
:disabled="props.disabled || installingJava || testingJavaSuccess === true"
|
||||
@click="reinstallJava"
|
||||
>
|
||||
<DownloadIcon />
|
||||
{{
|
||||
installingJava
|
||||
? formatMessage(messages.installing)
|
||||
: formatMessage(messages.installRecommended)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button class="!shadow-none" :disabled="props.disabled" @click="autoDetect">
|
||||
<SearchIcon />
|
||||
{{ formatMessage(messages.detect) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button class="!shadow-none" :disabled="props.disabled" @click="handleJavaFileInput()">
|
||||
<FolderSearchIcon />
|
||||
{{ formatMessage(messages.browse) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<DownloadIcon />
|
||||
{{
|
||||
installingJava
|
||||
? formatMessage(messages.installing)
|
||||
: formatMessage(messages.installRecommended)
|
||||
}}
|
||||
</Button>
|
||||
<Button :disabled="props.disabled" @click="autoDetect">
|
||||
<SearchIcon />
|
||||
{{ formatMessage(messages.detect) }}
|
||||
</Button>
|
||||
<Button :disabled="props.disabled" @click="handleJavaFileInput()">
|
||||
<FolderSearchIcon />
|
||||
{{ formatMessage(messages.browse) }}
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Button } from '@modrinth/ui'
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
DownloadIcon,
|
||||
@@ -90,7 +99,6 @@ import {
|
||||
XCircleIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
StyledInput,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import { IconButton } from '@modrinth/ui'
|
||||
import { CheckIcon } from '@modrinth/assets'
|
||||
import { Badge, ButtonStyled } from '@modrinth/ui'
|
||||
import { Badge } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import { SwapIcon } from '@/assets/icons/index.js'
|
||||
@@ -74,18 +75,16 @@ const onHide = () => {
|
||||
@click="$router.push(`/project/${version.project_id}/version/${version.id}`)"
|
||||
>
|
||||
<div class="table-cell table-text">
|
||||
<ButtonStyled
|
||||
circular
|
||||
:color="version.id === installedVersion ? 'standard' : 'brand'"
|
||||
>
|
||||
<button
|
||||
<IconButton
|
||||
:type="version.id === installedVersion ? 'base' : 'colored'"
|
||||
:color="version.id === installedVersion ? undefined : 'brand'"
|
||||
label="Switch version"
|
||||
:disabled="inProgress || installing || version.id === installedVersion"
|
||||
@click.stop="() => switchVersion(version.id)"
|
||||
>
|
||||
<SwapIcon v-if="version.id !== installedVersion" />
|
||||
<CheckIcon v-else />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<SwapIcon v-if="version.id !== installedVersion" />
|
||||
<CheckIcon v-else />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div class="name-cell table-cell table-text">
|
||||
<div class="version-link">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { NotepadTextIcon, XIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, defineMessages, injectNotificationManager, useVIntl } from '@modrinth/ui'
|
||||
import { defineMessages, injectNotificationManager, useVIntl } from '@modrinth/ui'
|
||||
import { type } from '@tauri-apps/plugin-os'
|
||||
import { $fetch } from 'ofetch'
|
||||
import { onMounted, ref } from 'vue'
|
||||
@@ -193,18 +194,14 @@ onMounted(async () => {
|
||||
{{ formatMessage(messages.surveyFooter) }}
|
||||
</p>
|
||||
<div class="flex gap-2">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="openSurvey">
|
||||
<NotepadTextIcon />
|
||||
{{ formatMessage(messages.takeSurvey) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="dismissSurvey">
|
||||
<XIcon />
|
||||
{{ formatMessage(messages.surveyNoThanks) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="openSurvey">
|
||||
<NotepadTextIcon />
|
||||
{{ formatMessage(messages.takeSurvey) }}
|
||||
</Button>
|
||||
<Button @click="dismissSurvey">
|
||||
<XIcon />
|
||||
{{ formatMessage(messages.surveyNoThanks) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ButtonStyled, injectNotificationManager, ProjectCard } from '@modrinth/ui'
|
||||
import { injectNotificationManager, ProjectCard, Button } from '@modrinth/ui'
|
||||
import { ref } from 'vue'
|
||||
|
||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||
@@ -69,9 +69,7 @@ async function install() {
|
||||
</p>
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="install">Install</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="install">Install</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,34 +4,23 @@
|
||||
class="flex items-center gap-2 mr-1.5"
|
||||
data-tauri-drag-region-exclude
|
||||
>
|
||||
<ButtonStyled type="transparent" circular>
|
||||
<button class="relative expanded-button" @click="() => getCurrentWindow().minimize()">
|
||||
<MinimizeIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled type="transparent" circular>
|
||||
<button class="relative expanded-button" @click="() => getCurrentWindow().toggleMaximize()">
|
||||
<RestoreIcon v-if="isMaximized" />
|
||||
<MaximizeIcon v-else />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled
|
||||
type="transparent"
|
||||
color="red"
|
||||
color-fill="none"
|
||||
hover-color-fill="background"
|
||||
circular
|
||||
>
|
||||
<button class="relative expanded-button close-button" @click="handleClose">
|
||||
<XIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<IconButton type="quiet" label="Minimize window" class="relative expanded-button" @click="() => getCurrentWindow().minimize()">
|
||||
<MinimizeIcon />
|
||||
</IconButton>
|
||||
<IconButton type="quiet" label="Toggle maximize window" class="relative expanded-button" @click="() => getCurrentWindow().toggleMaximize()">
|
||||
<RestoreIcon v-if="isMaximized" />
|
||||
<MaximizeIcon v-else />
|
||||
</IconButton>
|
||||
<IconButton type="quiet" color="red" label="Close window" class="relative expanded-button close-button hover:!bg-red focus-visible:!bg-red" @click="handleClose">
|
||||
<XIcon />
|
||||
</IconButton>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { IconButton } from '@modrinth/ui'
|
||||
import { MaximizeIcon, MinimizeIcon, RestoreIcon, XIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled } from '@modrinth/ui'
|
||||
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<template>
|
||||
<ButtonStyled color="brand" type="outlined" hover-color-fill="background">
|
||||
<button
|
||||
<Button type="outlined"
|
||||
v-if="showUpdatePill"
|
||||
type="button"
|
||||
class="!h-[34px] text-sm !transition-[opacity,transform,background-color,color,filter] !duration-200 ease-out"
|
||||
native-type="button"
|
||||
class="!h-[34px] text-sm !transition-[opacity,transform,background-color,color,filter] !duration-200 ease-out !text-brand [&>svg]:!text-brand !shadow-[inset_0_0_0_1px_var(--color-brand)] hover:!bg-brand focus-visible:!bg-brand hover:!text-[var(--color-accent-contrast)] focus-visible:!text-[var(--color-accent-contrast)]"
|
||||
:class="{
|
||||
'opacity-0 scale-[0.96]': finishedDownloading && !animateReadyPill,
|
||||
'opacity-100 scale-100': finishedDownloading && animateReadyPill,
|
||||
@@ -12,20 +11,20 @@
|
||||
:aria-busy="isUpdateDownloading"
|
||||
@click="handleUpdateClick"
|
||||
>
|
||||
<RefreshCwIcon v-if="finishedDownloading" :class="{ 'animate-spin': restarting }" />
|
||||
<DownloadIcon v-else />
|
||||
<span v-if="isUpdateDownloading">
|
||||
{{ formatMessage(messages.downloadingUpdate) }}
|
||||
<span class="inline-block w-[3ch] text-right tabular-nums">{{ downloadPercent }}%</span>
|
||||
</span>
|
||||
<span v-else>{{ updateLabel }}</span>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<RefreshCwIcon v-if="finishedDownloading" :class="{ 'animate-spin': restarting }" />
|
||||
<DownloadIcon v-else />
|
||||
<span v-if="isUpdateDownloading">
|
||||
{{ formatMessage(messages.downloadingUpdate) }}
|
||||
<span class="inline-block w-[3ch] text-right tabular-nums">{{ downloadPercent }}%</span>
|
||||
</span>
|
||||
<span v-else>{{ updateLabel }}</span>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { DownloadIcon, RefreshCwIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
|
||||
|
||||
import {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { Button, IconButton } from '@modrinth/ui'
|
||||
import { MailIcon, SearchIcon, SendIcon, UserIcon, UserPlusIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
IntlFormatted,
|
||||
@@ -197,26 +197,20 @@ const messages = defineMessages({
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<template v-if="friend.id === userCredentials?.user_id">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="addFriend(friend)">
|
||||
<UserPlusIcon />
|
||||
Accept
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="removeFriend(friend)">
|
||||
<XIcon />
|
||||
Ignore
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="addFriend(friend)">
|
||||
<UserPlusIcon />
|
||||
Accept
|
||||
</Button>
|
||||
<Button @click="removeFriend(friend)">
|
||||
<XIcon />
|
||||
Ignore
|
||||
</Button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ButtonStyled>
|
||||
<button @click="removeFriend(friend)">
|
||||
<XIcon />
|
||||
Cancel
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="removeFriend(friend)">
|
||||
<XIcon />
|
||||
Cancel
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -240,26 +234,21 @@ const messages = defineMessages({
|
||||
wrapper-class="flex-1"
|
||||
@keyup.enter="addFriendFromModal"
|
||||
/>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="username.length === 0" @click="addFriendFromModal">
|
||||
<SendIcon />
|
||||
{{ formatMessage(messages.sendFriendRequest) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" :disabled="username.length === 0" @click="addFriendFromModal">
|
||||
<SendIcon />
|
||||
{{ formatMessage(messages.sendFriendRequest) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalWrapper>
|
||||
<div v-if="userCredentials && !loading" class="flex gap-1 items-center mb-3 -ml-1">
|
||||
<template v-if="sortedFriends.length > 0">
|
||||
<ButtonStyled circular type="transparent">
|
||||
<button
|
||||
<IconButton type="quiet" :label="formatMessage(messages.addFriend)"
|
||||
v-tooltip="formatMessage(messages.addFriend)"
|
||||
:aria-label="formatMessage(messages.addFriend)"
|
||||
@click="addFriendModal.show"
|
||||
>
|
||||
<UserPlusIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<UserPlusIcon />
|
||||
</IconButton>
|
||||
<StyledInput
|
||||
v-model="search"
|
||||
:icon="SearchIcon"
|
||||
@@ -274,23 +263,20 @@ const messages = defineMessages({
|
||||
<h3 v-else class="w-full text-base text-primary font-medium m-0">
|
||||
{{ formatMessage(messages.friends) }}
|
||||
</h3>
|
||||
<ButtonStyled v-if="incomingRequests.length > 0" circular type="transparent">
|
||||
<button
|
||||
<IconButton type="quiet" :label="formatMessage(messages.viewFriendRequests, { count: incomingRequests.length })" v-if="incomingRequests.length > 0"
|
||||
v-tooltip="formatMessage(messages.viewFriendRequests, { count: incomingRequests.length })"
|
||||
class="relative"
|
||||
:aria-label="formatMessage(messages.viewFriendRequests, { count: incomingRequests.length })"
|
||||
@click="friendInvitesModal.show"
|
||||
>
|
||||
<MailIcon />
|
||||
<span
|
||||
v-if="incomingRequests.length > 0"
|
||||
aria-hidden="true"
|
||||
class="absolute bg-brand text-brand-inverted text-[8px] top-0.5 px-1 right-0.5 min-w-3 h-3 rounded-full flex items-center justify-center font-bold"
|
||||
>
|
||||
{{ incomingRequests.length }}
|
||||
</span>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<MailIcon />
|
||||
<span
|
||||
v-if="incomingRequests.length > 0"
|
||||
aria-hidden="true"
|
||||
class="absolute bg-brand text-brand-inverted text-[8px] top-0.5 px-1 right-0.5 min-w-3 h-3 rounded-full flex items-center justify-center font-bold"
|
||||
>
|
||||
{{ incomingRequests.length }}
|
||||
</span>
|
||||
</IconButton>
|
||||
</div>
|
||||
<div class="flex flex-col gap-3">
|
||||
<h3 v-if="loading" class="text-base text-primary font-medium m-0">
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { IconButton, TeleportOverflowMenu } from '@modrinth/ui'
|
||||
import { MoreVerticalIcon, TrashIcon, UserIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Accordion,
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
OverflowMenu,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useTemplateRef } from 'vue'
|
||||
@@ -159,29 +158,26 @@ const messages = defineMessages({
|
||||
<span v-else-if="friend.status" class="m-0 text-xs">{{ friend.status }}</span>
|
||||
</div>
|
||||
</RouterLink>
|
||||
<ButtonStyled v-if="friend.accepted" circular type="transparent">
|
||||
<OverflowMenu
|
||||
<TeleportOverflowMenu type="quiet" label="More options" v-if="friend.accepted"
|
||||
class="opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
:options="[
|
||||
{
|
||||
id: 'remove-friend',
|
||||
label: formatMessage(messages.removeFriend),
|
||||
action: () => removeFriend(friend),
|
||||
color: 'red',
|
||||
tone: 'red',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<MoreVerticalIcon />
|
||||
<template #remove-friend>
|
||||
<TrashIcon />
|
||||
{{ formatMessage(messages.removeFriend) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else type="transparent" circular>
|
||||
<button v-tooltip="formatMessage(messages.cancelRequest)" @click="removeFriend(friend)">
|
||||
<XIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<MoreVerticalIcon />
|
||||
<template #remove-friend>
|
||||
<TrashIcon />
|
||||
{{ formatMessage(messages.removeFriend) }}
|
||||
</template>
|
||||
</TeleportOverflowMenu>
|
||||
<IconButton type="quiet" :label="formatMessage(messages.cancelRequest)" v-else v-tooltip="formatMessage(messages.cancelRequest)" @click="removeFriend(friend)">
|
||||
<XIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup>
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { CheckIcon, PlusIcon, SearchIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
injectNotificationManager,
|
||||
StyledInput,
|
||||
} from '@modrinth/ui'
|
||||
@@ -109,19 +109,15 @@ async function addServer(instance) {
|
||||
/>
|
||||
{{ instance.name }}
|
||||
</router-link>
|
||||
<ButtonStyled>
|
||||
<button :disabled="instance.added || instance.adding" @click="addServer(instance)">
|
||||
<PlusIcon v-if="!instance.added && !instance.adding" />
|
||||
<CheckIcon v-else-if="instance.added" />
|
||||
{{ instance.adding ? 'Adding...' : instance.added ? 'Added' : 'Add' }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button :disabled="instance.added || instance.adding" @click="addServer(instance)">
|
||||
<PlusIcon v-if="!instance.added && !instance.adding" />
|
||||
<CheckIcon v-else-if="instance.added" />
|
||||
{{ instance.adding ? 'Adding...' : instance.added ? 'Added' : 'Add' }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group push-right">
|
||||
<ButtonStyled>
|
||||
<button @click="modal.hide()">Cancel</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="modal.hide()">Cancel</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalWrapper>
|
||||
|
||||
@@ -85,72 +85,60 @@
|
||||
|
||||
<template #actions>
|
||||
<PageHeaderActions>
|
||||
<ButtonStyled v-if="isInstalling" color="brand" size="large">
|
||||
<button type="button" disabled>
|
||||
{{ formatMessage(commonMessages.installingLabel) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else-if="playing" color="red" size="large">
|
||||
<button type="button" :disabled="stopping" @click="emit('stop')">
|
||||
<StopCircleIcon />
|
||||
{{
|
||||
stopping ? formatMessage(messages.stopping) : formatMessage(commonMessages.stopButton)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else-if="instance.quarantined" color="brand" size="large">
|
||||
<button v-tooltip="formatMessage(messages.lockedPlayTooltip)" type="button" disabled>
|
||||
<PlayIcon />
|
||||
{{ formatMessage(commonMessages.playButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else-if="instance.install_stage !== 'installed'" color="brand" size="large">
|
||||
<button type="button" @click="emit('repair')">
|
||||
<DownloadIcon />
|
||||
{{ formatMessage(messages.repair) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<JoinedButtons
|
||||
<Button type="colored" color="brand" size="xl" v-if="isInstalling" native-type="button" disabled>
|
||||
{{ formatMessage(commonMessages.installingLabel) }}
|
||||
</Button>
|
||||
<Button type="colored" color="red" size="xl" v-else-if="playing" native-type="button" :disabled="stopping" @click="emit('stop')">
|
||||
<StopCircleIcon />
|
||||
{{
|
||||
stopping ? formatMessage(messages.stopping) : formatMessage(commonMessages.stopButton)
|
||||
}}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" size="xl" v-else-if="instance.quarantined" v-tooltip="formatMessage(messages.lockedPlayTooltip)" native-type="button" disabled>
|
||||
<PlayIcon />
|
||||
{{ formatMessage(commonMessages.playButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" size="xl" v-else-if="instance.install_stage !== 'installed'" native-type="button" @click="emit('repair')">
|
||||
<DownloadIcon />
|
||||
{{ formatMessage(messages.repair) }}
|
||||
</Button>
|
||||
<SplitButton
|
||||
v-else-if="!loading && isServerInstance"
|
||||
:actions="serverPlayActions"
|
||||
type="colored"
|
||||
color="brand"
|
||||
size="large"
|
||||
/>
|
||||
<ButtonStyled v-else-if="!loading" color="brand" size="large">
|
||||
<button type="button" @click="emit('play')">
|
||||
<PlayIcon />
|
||||
{{ formatMessage(commonMessages.playButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else color="brand" size="large">
|
||||
<button type="button" disabled>{{ formatMessage(messages.starting) }}</button>
|
||||
</ButtonStyled>
|
||||
size="xl"
|
||||
:options="serverPlayOptions"
|
||||
:menu-label="formatMessage(messages.launchInstance)"
|
||||
@click="emit('playServer')"
|
||||
>
|
||||
<PlayIcon />
|
||||
{{ formatMessage(commonMessages.playButton) }}
|
||||
</SplitButton>
|
||||
<Button type="colored" color="brand" size="xl" v-else-if="!loading" native-type="button" @click="emit('play')">
|
||||
<PlayIcon />
|
||||
{{ formatMessage(commonMessages.playButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" size="xl" v-else native-type="button" disabled>{{ formatMessage(messages.starting) }}</Button>
|
||||
|
||||
<ButtonStyled circular size="large">
|
||||
<button
|
||||
<IconButton size="xl" :label="formatMessage(messages.instanceSettings)"
|
||||
v-tooltip="formatMessage(messages.instanceSettings)"
|
||||
type="button"
|
||||
:aria-label="formatMessage(messages.instanceSettings)"
|
||||
native-type="button"
|
||||
@click="emit('settings')"
|
||||
>
|
||||
<SettingsIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular size="large" type="transparent">
|
||||
<TeleportOverflowMenu
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
<TeleportOverflowMenu type="quiet" size="xl" :label="formatMessage(messages.moreActions)"
|
||||
:options="moreActions"
|
||||
:tooltip="formatMessage(messages.moreActions)"
|
||||
:aria-label="formatMessage(messages.moreActions)"
|
||||
>
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</ButtonStyled>
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</PageHeaderActions>
|
||||
</template>
|
||||
</PageHeader>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, IconButton, SplitButton, TeleportOverflowMenu } from '@modrinth/ui'
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import {
|
||||
DownloadIcon,
|
||||
@@ -169,12 +157,9 @@ import {
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
formatLoaderLabel,
|
||||
type JoinedButtonAction,
|
||||
JoinedButtons,
|
||||
LoaderIcon as ServerLoaderIcon,
|
||||
PageHeader,
|
||||
PageHeaderActions,
|
||||
@@ -182,8 +167,7 @@ import {
|
||||
PageHeaderMetadata,
|
||||
PageHeaderMetadataItem,
|
||||
type ServerLoader,
|
||||
TeleportOverflowMenu,
|
||||
type TeleportOverflowMenuItem,
|
||||
type OverflowMenuOption,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed } from 'vue'
|
||||
@@ -350,13 +334,7 @@ const playtimeLabel = computed(() => {
|
||||
const seconds = Math.floor(props.timePlayed)
|
||||
return `${seconds} second${seconds > 1 ? 's' : ''}`
|
||||
})
|
||||
const serverPlayActions = computed<JoinedButtonAction[]>(() => [
|
||||
{
|
||||
id: 'join_server',
|
||||
label: formatMessage(commonMessages.playButton),
|
||||
icon: PlayIcon,
|
||||
action: () => emit('playServer'),
|
||||
},
|
||||
const serverPlayOptions = computed<OverflowMenuOption[]>(() => [
|
||||
{
|
||||
id: 'launch_instance',
|
||||
label: formatMessage(messages.launchInstance),
|
||||
@@ -364,8 +342,8 @@ const serverPlayActions = computed<JoinedButtonAction[]>(() => [
|
||||
action: () => emit('play'),
|
||||
},
|
||||
])
|
||||
const moreActions = computed<TeleportOverflowMenuItem[]>(() => {
|
||||
const actions: TeleportOverflowMenuItem[] = [
|
||||
const moreActions = computed<OverflowMenuOption[]>(() => {
|
||||
const actions: OverflowMenuOption[] = [
|
||||
{
|
||||
id: 'open-folder',
|
||||
label: formatMessage(messages.openFolder),
|
||||
@@ -393,12 +371,12 @@ const moreActions = computed<TeleportOverflowMenuItem[]>(() => {
|
||||
|
||||
if (props.instance.shared_instance?.role === 'member') {
|
||||
actions.push(
|
||||
{ divider: true },
|
||||
{ type: 'divider' },
|
||||
{
|
||||
id: 'report-shared-instance',
|
||||
label: formatMessage(commonMessages.reportButton),
|
||||
icon: ReportIcon,
|
||||
color: 'red',
|
||||
tone: 'red',
|
||||
action: (event) => emit('report', event),
|
||||
},
|
||||
)
|
||||
|
||||
+17
-18
@@ -6,23 +6,21 @@
|
||||
>
|
||||
{{ formatMessage(messages.sharedInstanceChangesBody) }}
|
||||
<template #actions>
|
||||
<ButtonStyled color="blue">
|
||||
<button class="!h-10" :disabled="isPublishButtonDisabled" @click="reviewChanges">
|
||||
<SpinnerIcon
|
||||
v-if="isReviewingPublish || isPublishing"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<UploadIcon v-else aria-hidden="true" />
|
||||
{{
|
||||
isPublishing
|
||||
? formatMessage(messages.sharedInstancePublishingButton)
|
||||
: isReviewingPublish
|
||||
? formatMessage(messages.sharedInstanceReviewingButton)
|
||||
: formatMessage(messages.sharedInstancePublishButton)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="blue" class="!h-10" :disabled="isPublishButtonDisabled" @click="reviewChanges">
|
||||
<SpinnerIcon
|
||||
v-if="isReviewingPublish || isPublishing"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<UploadIcon v-else aria-hidden="true" />
|
||||
{{
|
||||
isPublishing
|
||||
? formatMessage(messages.sharedInstancePublishingButton)
|
||||
: isReviewingPublish
|
||||
? formatMessage(messages.sharedInstanceReviewingButton)
|
||||
: formatMessage(messages.sharedInstancePublishButton)
|
||||
}}
|
||||
</Button>
|
||||
</template>
|
||||
</Admonition>
|
||||
|
||||
@@ -35,8 +33,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { SpinnerIcon, UploadIcon } from '@modrinth/assets'
|
||||
import { Admonition, ButtonStyled, useVIntl } from '@modrinth/ui'
|
||||
import { Admonition, useVIntl } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import SharedInstancePublishModal from '@/components/ui/shared-instances/SharedInstancePublishModal.vue'
|
||||
|
||||
+6
-7
@@ -8,19 +8,18 @@
|
||||
>
|
||||
{{ formatSharedInstanceUnavailable(reason ?? null, manager) }}
|
||||
<template v-if="reason === 'quarantined'" #actions>
|
||||
<ButtonStyled color="orange">
|
||||
<button class="!h-10" @click="emit('delete')">
|
||||
<TrashIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.deleteInstance) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="orange" class="!h-10" @click="emit('delete')">
|
||||
<TrashIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.deleteInstance) }}
|
||||
</Button>
|
||||
</template>
|
||||
</Admonition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { TrashIcon } from '@modrinth/assets'
|
||||
import { Admonition, ButtonStyled, defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { Admonition, defineMessages, useVIntl } from '@modrinth/ui'
|
||||
|
||||
import type { SharedInstanceUnavailableReason } from '@/helpers/install'
|
||||
import {
|
||||
|
||||
+6
-7
@@ -6,19 +6,18 @@
|
||||
>
|
||||
{{ formatMessage(messages.sharedInstanceUpdateAvailableBody, { name: instanceName }) }}
|
||||
<template #actions>
|
||||
<ButtonStyled color="blue">
|
||||
<button class="!h-10" @click="emit('review', $event)">
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.sharedInstanceReviewUpdateButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="blue" class="!h-10" @click="emit('review', $event)">
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.sharedInstanceReviewUpdateButton) }}
|
||||
</Button>
|
||||
</template>
|
||||
</Admonition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { DownloadIcon } from '@modrinth/assets'
|
||||
import { Admonition, ButtonStyled, useVIntl } from '@modrinth/ui'
|
||||
import { Admonition, useVIntl } from '@modrinth/ui'
|
||||
|
||||
import { instanceAdmonitionsMessages as messages } from './instance-admonitions-messages'
|
||||
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Button, TeleportOverflowMenu } from '@modrinth/ui'
|
||||
import { CopyIcon, EditIcon, PlusIcon, SpinnerIcon, TrashIcon, UploadIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
Chips,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
OverflowMenu,
|
||||
StyledInput,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { Avatar, Checkbox, Chips, defineMessages, injectNotificationManager, StyledInput, useVIntl } from '@modrinth/ui'
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
@@ -298,17 +289,20 @@ const messages = defineMessages({
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-lg font-semibold text-contrast">Icon</span>
|
||||
<div class="group relative w-fit">
|
||||
<OverflowMenu
|
||||
<TeleportOverflowMenu :label="formatMessage(messages.editIcon)"
|
||||
v-tooltip="formatMessage(messages.editIcon)"
|
||||
class="bg-transparent border-none appearance-none p-0 m-0 cursor-pointer group-active:scale-95 transition-transform"
|
||||
:options="[
|
||||
{
|
||||
id: 'select',
|
||||
label: icon
|
||||
? formatMessage(messages.replaceIcon)
|
||||
: formatMessage(messages.selectIcon),
|
||||
action: () => setIcon(),
|
||||
},
|
||||
{
|
||||
id: 'remove',
|
||||
color: 'danger',
|
||||
label: formatMessage(messages.removeIcon),
|
||||
action: () => resetIcon(),
|
||||
shown: !!icon,
|
||||
},
|
||||
@@ -331,7 +325,7 @@ const messages = defineMessages({
|
||||
{{ icon ? formatMessage(messages.replaceIcon) : formatMessage(messages.selectIcon) }}
|
||||
</template>
|
||||
<template #remove> <TrashIcon /> {{ formatMessage(messages.removeIcon) }} </template>
|
||||
</OverflowMenu>
|
||||
</TeleportOverflowMenu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -352,17 +346,15 @@ const messages = defineMessages({
|
||||
<h2 id="duplicate-instance-label" class="m-0 text-lg font-semibold text-contrast block">
|
||||
{{ formatMessage(messages.duplicateInstance) }}
|
||||
</h2>
|
||||
<ButtonStyled>
|
||||
<button
|
||||
<Button
|
||||
v-tooltip="installing ? formatMessage(messages.duplicateButtonTooltipInstalling) : null"
|
||||
aria-labelledby="duplicate-instance-label"
|
||||
:disabled="installing"
|
||||
class="w-max !shadow-none"
|
||||
class="w-max"
|
||||
@click="duplicateInstance"
|
||||
>
|
||||
<CopyIcon /> {{ formatMessage(messages.duplicateButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<CopyIcon /> {{ formatMessage(messages.duplicateButton) }}
|
||||
</Button>
|
||||
<p class="m-0">
|
||||
{{ formatMessage(messages.duplicateInstanceDescription) }}
|
||||
</p>
|
||||
@@ -388,11 +380,9 @@ const messages = defineMessages({
|
||||
class="w-full max-w-[300px]"
|
||||
@submit="() => addCategory"
|
||||
/>
|
||||
<ButtonStyled>
|
||||
<button class="w-fit !shadow-none" @click="() => addCategory()">
|
||||
<PlusIcon /> {{ formatMessage(messages.libraryGroupsCreate) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button class="w-fit" @click="() => addCategory()">
|
||||
<PlusIcon /> {{ formatMessage(messages.libraryGroupsCreate) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="m-0">
|
||||
@@ -421,22 +411,20 @@ const messages = defineMessages({
|
||||
<h2 id="delete-instance-label" class="m-0 text-lg font-semibold text-contrast block">
|
||||
{{ formatMessage(messages.deleteInstance) }}
|
||||
</h2>
|
||||
<ButtonStyled color="red">
|
||||
<button
|
||||
<Button type="colored" color="red"
|
||||
aria-labelledby="delete-instance-label"
|
||||
:disabled="removing"
|
||||
class="w-fit !shadow-none"
|
||||
class="w-fit"
|
||||
@click="deleteConfirmModal.show()"
|
||||
>
|
||||
<SpinnerIcon v-if="removing" class="animate-spin" />
|
||||
<TrashIcon v-else />
|
||||
{{
|
||||
removing
|
||||
? formatMessage(messages.deletingInstanceButton)
|
||||
: formatMessage(messages.deleteInstanceButton)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<SpinnerIcon v-if="removing" class="animate-spin" />
|
||||
<TrashIcon v-else />
|
||||
{{
|
||||
removing
|
||||
? formatMessage(messages.deletingInstanceButton)
|
||||
: formatMessage(messages.deleteInstanceButton)
|
||||
}}
|
||||
</Button>
|
||||
<p class="m-0">
|
||||
{{ formatMessage(messages.deleteInstanceDescription) }}
|
||||
</p>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
CoffeeIcon,
|
||||
@@ -9,7 +10,6 @@ import {
|
||||
XCircleIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
@@ -224,45 +224,56 @@ const messages = defineMessages({
|
||||
wrapper-class="flex-1 min-w-0"
|
||||
@update:model-value="(val) => (javaPath = String(val))"
|
||||
/>
|
||||
<ButtonStyled
|
||||
<Button
|
||||
type="quiet"
|
||||
:color="
|
||||
!hoveringTest && !testingJava
|
||||
? javaTestResult === true
|
||||
? 'green'
|
||||
: 'red'
|
||||
: 'standard'
|
||||
: undefined
|
||||
"
|
||||
color-fill="text"
|
||||
>
|
||||
<button
|
||||
:disabled="!overrideJavaInstall || testingJava"
|
||||
@click="testJavaInstallation(activePath, optimalJava?.parsed_version, true)"
|
||||
@mouseenter="overrideJavaInstall && (hoveringTest = true)"
|
||||
@mouseleave="hoveringTest = false"
|
||||
>
|
||||
<SpinnerIcon v-if="testingJava" class="animate-spin h-4 w-4" />
|
||||
<CheckCircleIcon
|
||||
v-else-if="javaTestResult === true && !hoveringTest"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
<XCircleIcon v-else-if="javaTestResult !== true && !hoveringTest" class="h-4 w-4" />
|
||||
<RefreshCwIcon v-else-if="overrideJavaInstall" class="h-4 w-4" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
:style="{ '--legacy-button-color': (
|
||||
!hoveringTest && !testingJava
|
||||
? javaTestResult === true
|
||||
? 'green'
|
||||
: 'red'
|
||||
: 'standard'
|
||||
) && (
|
||||
!hoveringTest && !testingJava
|
||||
? javaTestResult === true
|
||||
? 'green'
|
||||
: 'red'
|
||||
: 'standard'
|
||||
) !== 'standard' ? `var(--color-${
|
||||
!hoveringTest && !testingJava
|
||||
? javaTestResult === true
|
||||
? 'green'
|
||||
: 'red'
|
||||
: 'standard'
|
||||
})` : undefined }" class="!text-[var(--legacy-button-color,var(--color-base))] [&>svg]:!text-[var(--legacy-button-color,var(--color-primary))]">
|
||||
<SpinnerIcon v-if="testingJava" class="animate-spin h-4 w-4" />
|
||||
<CheckCircleIcon
|
||||
v-else-if="javaTestResult === true && !hoveringTest"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
<XCircleIcon v-else-if="javaTestResult !== true && !hoveringTest" class="h-4 w-4" />
|
||||
<RefreshCwIcon v-else-if="overrideJavaInstall" class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div v-if="overrideJavaInstall" class="flex gap-2">
|
||||
<ButtonStyled>
|
||||
<button @click="handleDetectJava">
|
||||
<SearchIcon />
|
||||
Detect
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="handleBrowseJava">
|
||||
<FolderSearchIcon />
|
||||
Browse
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="handleDetectJava">
|
||||
<SearchIcon />
|
||||
Detect
|
||||
</Button>
|
||||
<Button @click="handleBrowseJava">
|
||||
<FolderSearchIcon />
|
||||
Browse
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,29 +40,26 @@
|
||||
</template>
|
||||
<template #cell-actions="{ row }">
|
||||
<div class="flex justify-end">
|
||||
<ButtonStyled circular type="transparent">
|
||||
<button
|
||||
v-tooltip="formatMessage(messages.revokeInvite)"
|
||||
:aria-label="
|
||||
<IconButton type="quiet" :label="
|
||||
formatMessage(messages.revokeInviteWithCode, {
|
||||
code: row.id,
|
||||
})
|
||||
"
|
||||
v-tooltip="formatMessage(messages.revokeInvite)"
|
||||
:disabled="revokeInviteMutation.isPending.value || isBusy"
|
||||
class="text-secondary hover:!filter-none hover:text-red focus-visible:!filter-none"
|
||||
@click="revokeInviteModal?.show(row.id)"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="
|
||||
revokeInviteMutation.isPending.value &&
|
||||
revokeInviteMutation.variables.value?.inviteId === row.id
|
||||
"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<XIcon v-else aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<SpinnerIcon
|
||||
v-if="
|
||||
revokeInviteMutation.isPending.value &&
|
||||
revokeInviteMutation.variables.value?.inviteId === row.id
|
||||
"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<XIcon v-else aria-hidden="true" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</template>
|
||||
</Table>
|
||||
@@ -80,9 +77,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { IconButton } from '@modrinth/ui'
|
||||
import { SpinnerIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
CopyCode,
|
||||
defineMessages,
|
||||
Table,
|
||||
|
||||
+12
-17
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Button, ButtonLink, IconButton } from '@modrinth/ui'
|
||||
import {
|
||||
CheckIcon,
|
||||
CopyIcon,
|
||||
@@ -7,7 +8,7 @@ import {
|
||||
MessagesSquareIcon,
|
||||
WrenchIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { Admonition, ButtonStyled, Collapsible, NewModal } from '@modrinth/ui'
|
||||
import { Admonition, Collapsible, NewModal } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import { hide_ads_window, show_ads_window } from '@/helpers/ads.js'
|
||||
@@ -137,16 +138,12 @@ async function copyToClipboard(text: string) {
|
||||
|
||||
<!-- Action buttons -->
|
||||
<div class="flex items-center gap-2">
|
||||
<ButtonStyled>
|
||||
<a href="https://support.modrinth.com" class="!w-full" @click="modal?.hide()">
|
||||
<MessagesSquareIcon /> Contact support
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="loadingSignIn" class="!w-full" @click="signInAgain">
|
||||
<LogInIcon /> Sign in again
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonLink href="https://support.modrinth.com" class="!w-full" @click="modal?.hide()">
|
||||
<MessagesSquareIcon /> Contact support
|
||||
</ButtonLink>
|
||||
<Button type="colored" color="brand" :disabled="loadingSignIn" class="!w-full" @click="signInAgain">
|
||||
<LogInIcon /> Sign in again
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
@@ -176,16 +173,14 @@ async function copyToClipboard(text: string) {
|
||||
>
|
||||
{{ debugInfo }}
|
||||
</div>
|
||||
<ButtonStyled circular>
|
||||
<button
|
||||
<IconButton :label="'Copy debug info'"
|
||||
v-tooltip="'Copy debug info'"
|
||||
:disabled="copied"
|
||||
@click="copyToClipboard(debugInfo)"
|
||||
>
|
||||
<template v-if="copied"> <CheckIcon class="text-green" /> </template>
|
||||
<template v-else> <CopyIcon /> </template>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<template v-if="copied"> <CheckIcon class="text-green" /> </template>
|
||||
<template v-else> <CopyIcon /> </template>
|
||||
</IconButton>
|
||||
</div>
|
||||
</Collapsible>
|
||||
</div>
|
||||
|
||||
+30
-33
@@ -21,38 +21,34 @@
|
||||
|
||||
<div class="flex flex-col gap-6 px-6 pb-6">
|
||||
<div class="flex justify-end gap-2">
|
||||
<ButtonStyled>
|
||||
<a class="w-full !shadow-none" href="https://support.modrinth.com" @click="modal?.hide()">
|
||||
<MessagesSquareIcon />
|
||||
{{ formatMessage(messages.getSupport) }}
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button class="w-full !shadow-none" :disabled="loadingSignIn" @click="signIn">
|
||||
<SpinnerIcon v-if="loadingSignIn" class="animate-spin" />
|
||||
<svg
|
||||
v-else
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="9.25" height="9.25" fill="black" fill-opacity="0.9" />
|
||||
<rect x="10.75" width="9.25" height="9.25" fill="black" fill-opacity="0.9" />
|
||||
<rect y="10.75" width="9.25" height="9.25" fill="black" fill-opacity="0.9" />
|
||||
<rect
|
||||
x="10.75"
|
||||
y="10.75"
|
||||
width="9.25"
|
||||
height="9.25"
|
||||
fill="black"
|
||||
fill-opacity="0.9"
|
||||
/>
|
||||
</svg>
|
||||
{{ formatMessage(messages.signIn) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonLink class="w-full" href="https://support.modrinth.com" @click="modal?.hide()">
|
||||
<MessagesSquareIcon />
|
||||
{{ formatMessage(messages.getSupport) }}
|
||||
</ButtonLink>
|
||||
<Button type="colored" color="brand" class="w-full" :disabled="loadingSignIn" @click="signIn">
|
||||
<SpinnerIcon v-if="loadingSignIn" class="animate-spin" />
|
||||
<svg
|
||||
v-else
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="9.25" height="9.25" fill="black" fill-opacity="0.9" />
|
||||
<rect x="10.75" width="9.25" height="9.25" fill="black" fill-opacity="0.9" />
|
||||
<rect y="10.75" width="9.25" height="9.25" fill="black" fill-opacity="0.9" />
|
||||
<rect
|
||||
x="10.75"
|
||||
y="10.75"
|
||||
width="9.25"
|
||||
height="9.25"
|
||||
fill="black"
|
||||
fill-opacity="0.9"
|
||||
/>
|
||||
</svg>
|
||||
{{ formatMessage(messages.signIn) }}
|
||||
</Button>
|
||||
</div>
|
||||
<p class="m-0 text-center text-sm text-secondary">
|
||||
{{ formatMessage(messages.dontHaveAccount) }}
|
||||
@@ -68,8 +64,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, ButtonLink } from '@modrinth/ui'
|
||||
import { MessagesSquareIcon, SpinnerIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, defineMessages, NewModal, useVIntl } from '@modrinth/ui'
|
||||
import { defineMessages, NewModal, useVIntl } from '@modrinth/ui'
|
||||
import { inject, type Ref, ref } from 'vue'
|
||||
|
||||
import steveImage from '@/assets/steve-look-up-left.webp'
|
||||
|
||||
@@ -6,28 +6,24 @@
|
||||
|
||||
<template #actions>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="modal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="confirm">
|
||||
<TrashIcon />
|
||||
{{ formatMessage(messages.deleteButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" @click="modal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="red" @click="confirm">
|
||||
<TrashIcon />
|
||||
{{ formatMessage(messages.deleteButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { TrashIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
NewModal,
|
||||
|
||||
@@ -18,12 +18,10 @@
|
||||
{{ formatMessage(messages.sharedInstance) }}
|
||||
</span>
|
||||
|
||||
<ButtonStyled type="transparent">
|
||||
<button @click="openViewContents">
|
||||
<EyeIcon />
|
||||
{{ formatMessage(messages.viewContents) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" @click="openViewContents">
|
||||
<EyeIcon />
|
||||
{{ formatMessage(messages.viewContents) }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 rounded-2xl bg-surface-2 p-3">
|
||||
@@ -113,40 +111,30 @@
|
||||
</p>
|
||||
|
||||
<div class="flex w-full items-center justify-between gap-2">
|
||||
<ButtonStyled type="transparent" color="red">
|
||||
<button @click="handleReport">
|
||||
<ReportIcon />
|
||||
{{ formatMessage(commonMessages.reportButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" color="red" @click="handleReport">
|
||||
<ReportIcon />
|
||||
{{ formatMessage(commonMessages.reportButton) }}
|
||||
</Button>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<template v-if="hasExternalFiles">
|
||||
<ButtonStyled type="transparent" color="orange">
|
||||
<button @click="handleAccept">
|
||||
{{ formatMessage(messages.installAnyway) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="handleDecline">
|
||||
<BanIcon />
|
||||
{{ formatMessage(messages.dontInstall) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" color="orange" @click="handleAccept">
|
||||
{{ formatMessage(messages.installAnyway) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" @click="handleDecline">
|
||||
<BanIcon />
|
||||
{{ formatMessage(messages.dontInstall) }}
|
||||
</Button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ButtonStyled>
|
||||
<button @click="handleDecline">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="handleAccept">
|
||||
<DownloadIcon />
|
||||
{{ formatMessage(messages.installButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="handleDecline">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" @click="handleAccept">
|
||||
<DownloadIcon />
|
||||
{{ formatMessage(messages.installButton) }}
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -161,12 +149,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { BanIcon, DownloadIcon, EyeIcon, ReportIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
type ContentItem,
|
||||
defineMessages,
|
||||
|
||||
@@ -10,33 +10,27 @@
|
||||
|
||||
<template #actions>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="handleCancel">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="handleGoToInstance">
|
||||
{{ formatMessage(messages.instance) }}
|
||||
<RightArrowIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="handleCreateAnyway">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.create) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" @click="handleCancel">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button @click="handleGoToInstance">
|
||||
{{ formatMessage(messages.instance) }}
|
||||
<RightArrowIcon />
|
||||
</Button>
|
||||
<Button type="colored" color="orange" @click="handleCreateAnyway">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.create) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { PlusIcon, RightArrowIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
IntlFormatted,
|
||||
|
||||
@@ -19,18 +19,14 @@
|
||||
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<ButtonStyled>
|
||||
<button class="w-full !shadow-none" type="button" @click="authenticate('sign-up')">
|
||||
<UserPlusIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.createAccountButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button class="w-full" type="button" @click="authenticate('sign-in')">
|
||||
<LogInIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.signInButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button class="w-full" native-type="button" @click="authenticate('sign-up')">
|
||||
<UserPlusIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.createAccountButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" class="w-full" native-type="button" @click="authenticate('sign-in')">
|
||||
<LogInIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.signInButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p class="m-0 text-center text-base font-medium leading-6 text-primary">
|
||||
@@ -69,23 +65,19 @@
|
||||
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<ButtonStyled type="outlined">
|
||||
<button class="w-full" type="button" @click="modal?.hide()">
|
||||
<XIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button
|
||||
class="w-full !shadow-none"
|
||||
type="button"
|
||||
<Button type="outlined" class="w-full" native-type="button" @click="modal?.hide()">
|
||||
<XIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.cancelButton) }}
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full"
|
||||
native-type="button"
|
||||
:disabled="reopeningBrowser"
|
||||
@click="reopenBrowser"
|
||||
>
|
||||
<RefreshCwIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.openBrowserAgainButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<RefreshCwIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.openBrowserAgainButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p class="m-0 text-center text-base font-medium leading-6 text-primary">
|
||||
@@ -107,8 +99,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { LogInIcon, RefreshCwIcon, SpinnerIcon, UserPlusIcon, XIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, defineMessages, IntlFormatted, NewModal, useVIntl } from '@modrinth/ui'
|
||||
import { defineMessages, IntlFormatted, NewModal, useVIntl } from '@modrinth/ui'
|
||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||
import { ref } from 'vue'
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { Settings2Icon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
injectPageContext,
|
||||
@@ -78,12 +78,10 @@ watch(
|
||||
{{ formatMessage(messages.adsConsentTitle) }}
|
||||
</h2>
|
||||
<div class="mt-2 flex flex-col gap-2.5 items-start">
|
||||
<ButtonStyled>
|
||||
<button class="!shadow-none" @click="manageAdsPreferences">
|
||||
<Settings2Icon aria-hidden="true" />
|
||||
{{ formatMessage(messages.adsConsentManage) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="manageAdsPreferences">
|
||||
<Settings2Icon aria-hidden="true" />
|
||||
{{ formatMessage(messages.adsConsentManage) }}
|
||||
</Button>
|
||||
<div>
|
||||
{{ formatMessage(messages.adsConsentIntro) }}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ButtonStyled, Toggle } from '@modrinth/ui'
|
||||
import { Toggle, Button } from '@modrinth/ui'
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
import { get as getSettings, set as setSettings } from '@/helpers/settings.ts'
|
||||
@@ -33,14 +33,12 @@ watch(
|
||||
</h2>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<ButtonStyled type="transparent">
|
||||
<button
|
||||
<Button type="quiet"
|
||||
:disabled="themeStore.getFeatureFlag(option) === DEFAULT_FEATURE_FLAGS[option]"
|
||||
@click="setFeatureFlag(option, DEFAULT_FEATURE_FLAGS[option])"
|
||||
>
|
||||
Reset to default
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
Reset to default
|
||||
</Button>
|
||||
<Toggle
|
||||
id="advanced-rendering"
|
||||
:model-value="themeStore.getFeatureFlag(option)"
|
||||
|
||||
+4
-7
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { IconButton } from '@modrinth/ui'
|
||||
import { BoxIcon, FolderOpenIcon, FolderSearchIcon, TrashIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
Slider,
|
||||
@@ -183,16 +183,13 @@ async function findLauncherDir() {
|
||||
wrapper-class="w-full"
|
||||
>
|
||||
<template #right>
|
||||
<ButtonStyled circular>
|
||||
<button
|
||||
<IconButton :label="formatMessage(messages.browseAppDirectory)"
|
||||
v-tooltip="formatMessage(messages.browseAppDirectory)"
|
||||
:aria-label="formatMessage(messages.browseAppDirectory)"
|
||||
class="ml-1.5"
|
||||
@click="findLauncherDir"
|
||||
>
|
||||
<FolderSearchIcon aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<FolderSearchIcon aria-hidden="true" />
|
||||
</IconButton>
|
||||
</template>
|
||||
</StyledInput>
|
||||
<p class="m-0 leading-tight text-secondary">
|
||||
|
||||
+9
-13
@@ -10,28 +10,24 @@
|
||||
|
||||
<template #actions>
|
||||
<div class="flex justify-end gap-2">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="modal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="confirm">
|
||||
<XIcon />
|
||||
{{ formatMessage(messages.revokeButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" @click="modal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="red" @click="confirm">
|
||||
<XIcon />
|
||||
{{ formatMessage(messages.revokeButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
IntlFormatted,
|
||||
|
||||
+13
-19
@@ -10,33 +10,27 @@
|
||||
|
||||
<template #actions>
|
||||
<div class="flex justify-end gap-2">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="handleCancel">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="handleGoToInstance">
|
||||
{{ formatMessage(messages.instance) }}
|
||||
<RightArrowIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="orange">
|
||||
<button @click="handleInstallAnyway">
|
||||
<DownloadIcon />
|
||||
{{ formatMessage(messages.installAnyway) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" @click="handleCancel">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button @click="handleGoToInstance">
|
||||
{{ formatMessage(messages.instance) }}
|
||||
<RightArrowIcon />
|
||||
</Button>
|
||||
<Button type="colored" color="orange" @click="handleInstallAnyway">
|
||||
<DownloadIcon />
|
||||
{{ formatMessage(messages.installAnyway) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { DownloadIcon, RightArrowIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
IntlFormatted,
|
||||
|
||||
+22
-41
@@ -2,13 +2,11 @@
|
||||
<div v-if="canUnpublish" class="flex flex-col gap-2.5">
|
||||
<span class="text-lg font-semibold text-contrast">{{ formatMessage(messages.title) }}</span>
|
||||
<div>
|
||||
<ButtonStyled color="orange">
|
||||
<button :disabled="busy" @click="unpublishModal?.show()">
|
||||
<SpinnerIcon v-if="unpublishing" class="animate-spin" />
|
||||
<UnlinkIcon v-else class="size-5" />
|
||||
{{ formatMessage(unpublishing ? messages.unpublishingButton : messages.unpublishButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="orange" :disabled="busy" @click="unpublishModal?.show()">
|
||||
<SpinnerIcon v-if="unpublishing" class="animate-spin" />
|
||||
<UnlinkIcon v-else class="size-5" />
|
||||
{{ formatMessage(unpublishing ? messages.unpublishingButton : messages.unpublishButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
<span class="text-primary">{{ formatMessage(messages.unpublishDescription) }}</span>
|
||||
</div>
|
||||
@@ -17,13 +15,11 @@
|
||||
formatMessage(messages.linkedTitle)
|
||||
}}</span>
|
||||
<div>
|
||||
<ButtonStyled color="orange">
|
||||
<button :disabled="busy" @click="unlinkModal?.show()">
|
||||
<SpinnerIcon v-if="unlinking" class="animate-spin" />
|
||||
<UnlinkIcon v-else class="size-5" />
|
||||
{{ formatMessage(unlinking ? messages.unlinkingButton : messages.unlinkButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="orange" :disabled="busy" @click="unlinkModal?.show()">
|
||||
<SpinnerIcon v-if="unlinking" class="animate-spin" />
|
||||
<UnlinkIcon v-else class="size-5" />
|
||||
{{ formatMessage(unlinking ? messages.unlinkingButton : messages.unlinkButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
<span class="text-primary">{{ formatMessage(messages.unlinkDescription) }}</span>
|
||||
</div>
|
||||
@@ -39,15 +35,11 @@
|
||||
}}</Admonition>
|
||||
<template #actions
|
||||
><div class="flex justify-end gap-2">
|
||||
<ButtonStyled type="outlined"
|
||||
><button class="!border" @click="unpublishModal?.hide()">
|
||||
<XIcon />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button></ButtonStyled
|
||||
><ButtonStyled color="orange"
|
||||
><button :disabled="busy" @click="confirmUnpublish">
|
||||
<UnlinkIcon />{{ formatMessage(messages.unpublishButton) }}
|
||||
</button></ButtonStyled
|
||||
>
|
||||
<Button type="outlined" class="!border" @click="unpublishModal?.hide()">
|
||||
<XIcon />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button><Button type="colored" color="orange" :disabled="busy" @click="confirmUnpublish">
|
||||
<UnlinkIcon />{{ formatMessage(messages.unpublishButton) }}
|
||||
</Button>
|
||||
</div></template
|
||||
>
|
||||
</NewModal>
|
||||
@@ -70,31 +62,20 @@
|
||||
</div>
|
||||
<template #actions
|
||||
><div class="flex justify-end gap-2">
|
||||
<ButtonStyled type="outlined"
|
||||
><button class="!border" @click="unlinkModal?.hide()">
|
||||
<XIcon />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button></ButtonStyled
|
||||
><ButtonStyled color="orange"
|
||||
><button :disabled="busy || backupBusy" @click="confirmUnlink">
|
||||
<UnlinkIcon />{{ formatMessage(messages.unlinkButton) }}
|
||||
</button></ButtonStyled
|
||||
>
|
||||
<Button type="outlined" class="!border" @click="unlinkModal?.hide()">
|
||||
<XIcon />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button><Button type="colored" color="orange" :disabled="busy || backupBusy" @click="confirmUnlink">
|
||||
<UnlinkIcon />{{ formatMessage(messages.unlinkButton) }}
|
||||
</Button>
|
||||
</div></template
|
||||
>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { SpinnerIcon, UnlinkIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
InlineBackupCreator,
|
||||
NewModal,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { Admonition, commonMessages, defineMessages, InlineBackupCreator, NewModal, useVIntl } from '@modrinth/ui'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
|
||||
@@ -26,12 +26,13 @@
|
||||
</span>
|
||||
</template>
|
||||
<div class="flex min-w-0 flex-col gap-3 pt-4">
|
||||
<div class="max-h-[292px] overflow-y-auto rounded-[20px]">
|
||||
<div ref="configFileTreeContainer" class="max-h-[292px] overflow-y-auto rounded-[20px]">
|
||||
<FileTreeSelect
|
||||
v-model="selectedConfigPaths"
|
||||
:items="configFileItems"
|
||||
:show-size="false"
|
||||
:show-modified="false"
|
||||
@navigate="scrollConfigFileTreeToTop"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,6 +76,7 @@ const emit = defineEmits<{
|
||||
const { formatMessage } = useVIntl()
|
||||
const { notifySharedInstanceError, notifySharedInstanceUnavailable } = useSharedInstanceErrors()
|
||||
const publishReviewModal = ref<InstanceType<typeof ContentDiffModal>>()
|
||||
const configFileTreeContainer = ref<HTMLElement>()
|
||||
const publishDiffs = ref<ContentDiffItem[]>([])
|
||||
const configFilePaths = ref<string[]>([])
|
||||
const selectedConfigPaths = ref<string[]>([])
|
||||
@@ -132,6 +134,12 @@ async function publishChanges() {
|
||||
}
|
||||
}
|
||||
|
||||
function scrollConfigFileTreeToTop() {
|
||||
if (configFileTreeContainer.value) {
|
||||
configFileTreeContainer.value.scrollTop = 0
|
||||
}
|
||||
}
|
||||
|
||||
function finishReview() {
|
||||
if (state.value === 'reviewing') {
|
||||
setState('idle')
|
||||
|
||||
+24
-38
@@ -193,53 +193,39 @@
|
||||
{{ formatMessage(messages.reviewedFiles) }}
|
||||
</p>
|
||||
<div v-if="!reportMode" class="flex w-full items-center justify-between gap-2">
|
||||
<ButtonStyled color="red" type="transparent">
|
||||
<button @click="reportMode = true">
|
||||
<ReportIcon />{{ formatMessage(commonMessages.reportButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" color="red" @click="reportMode = true">
|
||||
<ReportIcon />{{ formatMessage(commonMessages.reportButton) }}
|
||||
</Button>
|
||||
<div class="flex items-center gap-2">
|
||||
<template v-if="hasExternalFiles">
|
||||
<ButtonStyled type="transparent" color="orange">
|
||||
<button @click="accept">
|
||||
{{ formatMessage(messages.installAnyway) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="handleCancel">
|
||||
<BanIcon />{{ formatMessage(messages.dontInstall) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" color="orange" @click="accept">
|
||||
{{ formatMessage(messages.installAnyway) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" @click="handleCancel">
|
||||
<BanIcon />{{ formatMessage(messages.dontInstall) }}
|
||||
</Button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ButtonStyled type="outlined">
|
||||
<button class="!border" @click="handleCancel">
|
||||
<XIcon />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="accept">
|
||||
<DownloadIcon />{{ formatMessage(messages.installButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" class="!border" @click="handleCancel">
|
||||
<XIcon />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" @click="accept">
|
||||
<DownloadIcon />{{ formatMessage(messages.installButton) }}
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="reportMode" #actions>
|
||||
<div class="flex justify-end gap-2">
|
||||
<ButtonStyled type="outlined">
|
||||
<button class="!border" :disabled="submitLoading" @click="handleCancel">
|
||||
<XIcon />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="!canSubmitReport" @click="submitReport">
|
||||
<SpinnerIcon v-if="submitLoading" class="animate-spin" />
|
||||
<SendIcon v-else />
|
||||
{{ formatMessage(commonMessages.reportButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" class="!border" :disabled="submitLoading" @click="handleCancel">
|
||||
<XIcon />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" :disabled="!canSubmitReport" @click="submitReport">
|
||||
<SpinnerIcon v-if="submitLoading" class="animate-spin" />
|
||||
<SendIcon v-else />
|
||||
{{ formatMessage(commonMessages.reportButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
@@ -252,6 +238,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { BanIcon, DownloadIcon, ReportIcon, SendIcon, SpinnerIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
@@ -259,7 +246,6 @@ import {
|
||||
AutoLink,
|
||||
Avatar,
|
||||
blockedUsersQueryKey,
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
Combobox,
|
||||
type ComboboxOption,
|
||||
|
||||
+6
-7
@@ -4,12 +4,10 @@
|
||||
<span class="font-semibold text-contrast">{{
|
||||
heading ?? formatMessage(messages.sharedInstance)
|
||||
}}</span>
|
||||
<ButtonStyled type="transparent">
|
||||
<button @click="emit('viewContents')">
|
||||
<EyeIcon />
|
||||
{{ formatMessage(messages.viewContents) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" @click="emit('viewContents')">
|
||||
<EyeIcon />
|
||||
{{ formatMessage(messages.viewContents) }}
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 rounded-2xl bg-surface-2 p-3">
|
||||
<Avatar
|
||||
@@ -33,8 +31,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { EyeIcon } from '@modrinth/assets'
|
||||
import { Avatar, ButtonStyled, defineMessages, formatLoader, useVIntl } from '@modrinth/ui'
|
||||
import { Avatar, defineMessages, formatLoader, useVIntl } from '@modrinth/ui'
|
||||
import { computed, toRefs } from 'vue'
|
||||
|
||||
import type { SharedInstanceInstallPreview } from '@/helpers/install'
|
||||
|
||||
@@ -22,11 +22,9 @@
|
||||
<div class="flex flex-col gap-4 w-full min-h-[20rem]">
|
||||
<section v-if="mode === 'edit' && canEditTextureAndModel">
|
||||
<h2 class="text-base font-semibold mb-2">{{ formatMessage(messages.textureSection) }}</h2>
|
||||
<ButtonStyled>
|
||||
<button class="!shadow-none" @click="openTextureFileBrowser">
|
||||
<UploadIcon /> {{ formatMessage(messages.replaceTextureButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="openTextureFileBrowser">
|
||||
<UploadIcon /> {{ formatMessage(messages.replaceTextureButton) }}
|
||||
</Button>
|
||||
<input
|
||||
ref="textureFileInput"
|
||||
type="file"
|
||||
@@ -112,28 +110,24 @@
|
||||
|
||||
<template #actions>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<ButtonStyled type="outlined">
|
||||
<button :disabled="isSaving" @click="hide">
|
||||
<XIcon />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button v-tooltip="saveTooltip" :disabled="disableSave || isSaving" @click="save">
|
||||
<SpinnerIcon v-if="isSaving" class="animate-spin" />
|
||||
<CheckIcon v-else-if="mode === 'new'" />
|
||||
<SaveIcon v-else />
|
||||
{{ formatMessage(mode === 'new' ? messages.addSkinButton : messages.saveSkinButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" :disabled="isSaving" @click="hide">
|
||||
<XIcon />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" v-tooltip="saveTooltip" :disabled="disableSave || isSaving" @click="save">
|
||||
<SpinnerIcon v-if="isSaving" class="animate-spin" />
|
||||
<CheckIcon v-else-if="mode === 'new'" />
|
||||
<SaveIcon v-else />
|
||||
{{ formatMessage(mode === 'new' ? messages.addSkinButton : messages.saveSkinButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { CheckIcon, SaveIcon, SpinnerIcon, UploadIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
CapeButton,
|
||||
CapeLikeTextButton,
|
||||
commonMessages,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { Button, IconButton } from '@modrinth/ui'
|
||||
import { DropdownIcon, EditIcon, PlusIcon, TrashIcon, UnknownIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Accordion,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
SkinButton,
|
||||
@@ -463,25 +463,20 @@ defineExpose({ getAddSkinButtonElement })
|
||||
@select="emit('select', skin)"
|
||||
>
|
||||
<template v-if="!readOnly" #overlay-buttons>
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
<Button type="colored" color="brand"
|
||||
:aria-label="formatMessage(messages.editSkinButton)"
|
||||
class="pointer-events-auto"
|
||||
@click.stop="(event: MouseEvent) => emit('edit', skin, event)"
|
||||
>
|
||||
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-show="!skin.is_equipped" circular color="red">
|
||||
<button
|
||||
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
|
||||
</Button>
|
||||
<IconButton type="colored" color="red" :label="formatMessage(messages.deleteSkinButton)" v-show="!skin.is_equipped"
|
||||
v-tooltip="formatMessage(messages.deleteSkinButton)"
|
||||
:aria-label="formatMessage(messages.deleteSkinButton)"
|
||||
class="!rounded-[100%] pointer-events-auto"
|
||||
@click.stop="emit('delete', skin)"
|
||||
>
|
||||
<TrashIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<TrashIcon />
|
||||
</IconButton>
|
||||
</template>
|
||||
</SkinButton>
|
||||
</div>
|
||||
@@ -503,25 +498,20 @@ defineExpose({ getAddSkinButtonElement })
|
||||
@select="emit('select', skin)"
|
||||
>
|
||||
<template v-if="!readOnly" #overlay-buttons>
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
<Button type="colored" color="brand"
|
||||
:aria-label="formatMessage(messages.editSkinButton)"
|
||||
class="pointer-events-auto"
|
||||
@click.stop="(event: MouseEvent) => emit('edit', skin, event)"
|
||||
>
|
||||
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-show="!skin.is_equipped" circular color="red">
|
||||
<button
|
||||
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
|
||||
</Button>
|
||||
<IconButton type="colored" color="red" :label="formatMessage(messages.deleteSkinButton)" v-show="!skin.is_equipped"
|
||||
v-tooltip="formatMessage(messages.deleteSkinButton)"
|
||||
:aria-label="formatMessage(messages.deleteSkinButton)"
|
||||
class="!rounded-[100%] pointer-events-auto"
|
||||
@click.stop="emit('delete', skin)"
|
||||
>
|
||||
<TrashIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<TrashIcon />
|
||||
</IconButton>
|
||||
</template>
|
||||
</SkinButton>
|
||||
</div>
|
||||
@@ -545,15 +535,13 @@ defineExpose({ getAddSkinButtonElement })
|
||||
@select="emit('select', skin)"
|
||||
>
|
||||
<template #overlay-buttons>
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
<Button type="colored" color="brand"
|
||||
:aria-label="formatMessage(messages.editSkinButton)"
|
||||
class="pointer-events-auto"
|
||||
@click.stop="(event: MouseEvent) => emit('edit', skin, event)"
|
||||
>
|
||||
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<EditIcon /> {{ formatMessage(commonMessages.editButton) }}
|
||||
</Button>
|
||||
</template>
|
||||
</SkinButton>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Button, TeleportOverflowMenu } from '@modrinth/ui'
|
||||
import {
|
||||
EyeIcon,
|
||||
FolderOpenIcon,
|
||||
@@ -9,10 +10,8 @@ import {
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
injectNotificationManager,
|
||||
OverflowMenu,
|
||||
SmartClickable,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
@@ -185,14 +184,11 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-1 justify-end smart-clickable:allow-pointer-events">
|
||||
<ButtonStyled v-if="playing && !loading" color="red">
|
||||
<button @click="stop">
|
||||
<StopCircleIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.stopButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else>
|
||||
<button
|
||||
<Button type="colored" color="red" v-if="playing && !loading" @click="stop">
|
||||
<StopCircleIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.stopButton) }}
|
||||
</Button>
|
||||
<Button v-else
|
||||
v-tooltip="
|
||||
instance.quarantined
|
||||
? 'This instance has been locked'
|
||||
@@ -203,36 +199,35 @@ onUnmounted(() => {
|
||||
:disabled="instance.quarantined || playing || loading"
|
||||
@click="play"
|
||||
>
|
||||
<SpinnerIcon v-if="loading" class="animate-spin" />
|
||||
<PlayIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.playButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu
|
||||
<SpinnerIcon v-if="loading" class="animate-spin" />
|
||||
<PlayIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.playButton) }}
|
||||
</Button>
|
||||
<TeleportOverflowMenu type="quiet" label="More options"
|
||||
:options="[
|
||||
{
|
||||
id: 'open-instance',
|
||||
label: 'View instance',
|
||||
shown: !!instance.id,
|
||||
action: () => router.push(encodeURI(`/instance/${instance.id}`)),
|
||||
},
|
||||
{
|
||||
id: 'open-folder',
|
||||
label: formatMessage(commonMessages.openFolderButton),
|
||||
action: () => showInstanceInFolder(instance.id),
|
||||
},
|
||||
]"
|
||||
>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #open-instance>
|
||||
<EyeIcon aria-hidden="true" />
|
||||
View instance
|
||||
</template>
|
||||
<template #open-folder>
|
||||
<FolderOpenIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.openFolderButton) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #open-instance>
|
||||
<EyeIcon aria-hidden="true" />
|
||||
View instance
|
||||
</template>
|
||||
<template #open-folder>
|
||||
<FolderOpenIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.openFolderButton) }}
|
||||
</template>
|
||||
</TeleportOverflowMenu>
|
||||
</div>
|
||||
</div>
|
||||
</SmartClickable>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Button, TeleportOverflowMenu } from '@modrinth/ui'
|
||||
import {
|
||||
ClipboardCopyIcon,
|
||||
EditIcon,
|
||||
@@ -21,11 +22,9 @@ import {
|
||||
import type { MessageDescriptor } from '@modrinth/ui'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
OverflowMenu,
|
||||
SmartClickable,
|
||||
TagItem,
|
||||
useFormatDateTime,
|
||||
@@ -412,17 +411,11 @@ const messages = defineMessages({
|
||||
</template>
|
||||
</div>
|
||||
<div class="flex gap-1 justify-end smart-clickable:allow-pointer-events">
|
||||
<ButtonStyled
|
||||
v-if="(playingWorld || (locked && playingInstance)) && !startingInstance"
|
||||
color="red"
|
||||
>
|
||||
<button @click="emit('stop')">
|
||||
<StopCircleIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.stopButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else>
|
||||
<button
|
||||
<Button type="colored" color="red" v-if="(playingWorld || (locked && playingInstance)) && !startingInstance" @click="emit('stop')">
|
||||
<StopCircleIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.stopButton) }}
|
||||
</Button>
|
||||
<Button v-else
|
||||
v-tooltip="
|
||||
quarantined
|
||||
? 'This instance has been locked'
|
||||
@@ -451,37 +444,40 @@ const messages = defineMessages({
|
||||
"
|
||||
@click="emit('play')"
|
||||
>
|
||||
<SpinnerIcon v-if="startingInstance && playingWorld" class="animate-spin" />
|
||||
<PlayIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.playButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu
|
||||
<SpinnerIcon v-if="startingInstance && playingWorld" class="animate-spin" />
|
||||
<PlayIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.playButton) }}
|
||||
</Button>
|
||||
<TeleportOverflowMenu type="quiet" label="More options"
|
||||
:options="[
|
||||
{
|
||||
id: 'play-instance',
|
||||
label: formatMessage(messages.playInstance),
|
||||
shown: !!instanceId,
|
||||
disabled: playingInstance || quarantined,
|
||||
action: () => emit('play-instance'),
|
||||
},
|
||||
{
|
||||
id: 'open-instance',
|
||||
label: formatMessage(messages.viewInstance),
|
||||
shown: !!instanceId,
|
||||
action: () => router.push(`/instance/${encodeURIComponent(instanceId)}`),
|
||||
},
|
||||
{
|
||||
id: 'refresh',
|
||||
label: formatMessage(commonMessages.refreshButton),
|
||||
shown: world.type === 'server',
|
||||
action: () => emit('refresh'),
|
||||
},
|
||||
{
|
||||
id: 'copy-address',
|
||||
label: formatMessage(messages.copyAddress),
|
||||
shown: world.type === 'server',
|
||||
action: () => copyToClipboard((world as ServerWorld).address),
|
||||
},
|
||||
{
|
||||
id: 'edit',
|
||||
label: formatMessage(commonMessages.editButton),
|
||||
action: () => emit('edit'),
|
||||
shown: !instanceId,
|
||||
disabled: locked || managed,
|
||||
@@ -493,15 +489,17 @@ const messages = defineMessages({
|
||||
},
|
||||
{
|
||||
id: 'open-folder',
|
||||
label: formatMessage(commonMessages.openFolderButton),
|
||||
shown: world.type === 'singleplayer',
|
||||
action: () => (world.type === 'singleplayer' ? emit('open-folder', world) : {}),
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
type: 'divider',
|
||||
shown: !!instanceId,
|
||||
},
|
||||
{
|
||||
id: 'dont-show-on-home',
|
||||
label: formatMessage(messages.dontShowOnHome),
|
||||
shown: !!instanceId,
|
||||
action: () => {
|
||||
set_world_display_status(
|
||||
@@ -516,17 +514,22 @@ const messages = defineMessages({
|
||||
},
|
||||
{
|
||||
id: 'create-shortcut',
|
||||
label: formatMessage(messages.createShortcut),
|
||||
shown: !!shortcutInstanceId && !quarantined,
|
||||
action: () => createShortcut(),
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
type: 'divider',
|
||||
shown: !instanceId,
|
||||
},
|
||||
{
|
||||
id: 'delete',
|
||||
color: 'red',
|
||||
hoverFilled: true,
|
||||
label: formatMessage(
|
||||
world.type === 'server'
|
||||
? commonMessages.removeButton
|
||||
: commonMessages.deleteLabel,
|
||||
),
|
||||
tone: 'red',
|
||||
action: () => emit('delete'),
|
||||
shown: !instanceId,
|
||||
disabled: locked || managed,
|
||||
@@ -538,51 +541,50 @@ const messages = defineMessages({
|
||||
},
|
||||
]"
|
||||
>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #play-instance>
|
||||
<PlayIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.playInstance) }}
|
||||
</template>
|
||||
<template #open-instance>
|
||||
<EyeIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.viewInstance) }}
|
||||
</template>
|
||||
<template #edit>
|
||||
<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) }}
|
||||
</template>
|
||||
<template #refresh>
|
||||
<UpdatedIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.refreshButton) }}
|
||||
</template>
|
||||
<template #create-shortcut>
|
||||
<ExternalIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.createShortcut) }}
|
||||
</template>
|
||||
<template #dont-show-on-home>
|
||||
<XIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.dontShowOnHome) }}
|
||||
</template>
|
||||
<template #delete>
|
||||
<TrashIcon aria-hidden="true" />
|
||||
{{
|
||||
formatMessage(
|
||||
world.type === 'server'
|
||||
? commonMessages.removeButton
|
||||
: commonMessages.deleteLabel,
|
||||
)
|
||||
}}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #play-instance>
|
||||
<PlayIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.playInstance) }}
|
||||
</template>
|
||||
<template #open-instance>
|
||||
<EyeIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.viewInstance) }}
|
||||
</template>
|
||||
<template #edit>
|
||||
<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) }}
|
||||
</template>
|
||||
<template #refresh>
|
||||
<UpdatedIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.refreshButton) }}
|
||||
</template>
|
||||
<template #create-shortcut>
|
||||
<ExternalIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.createShortcut) }}
|
||||
</template>
|
||||
<template #dont-show-on-home>
|
||||
<XIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.dontShowOnHome) }}
|
||||
</template>
|
||||
<template #delete>
|
||||
<TrashIcon aria-hidden="true" />
|
||||
{{
|
||||
formatMessage(
|
||||
world.type === 'server'
|
||||
? commonMessages.removeButton
|
||||
: commonMessages.deleteLabel,
|
||||
)
|
||||
}}
|
||||
</template>
|
||||
</TeleportOverflowMenu>
|
||||
</div>
|
||||
</div>
|
||||
</SmartClickable>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { PlayIcon, PlusIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
@@ -96,24 +96,18 @@ defineExpose({ show, hide })
|
||||
/>
|
||||
<template #actions>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button :disabled="!address" @click="addServer(false)">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.addServer) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="!address" @click="addServer(true)">
|
||||
<PlayIcon />
|
||||
{{ formatMessage(messages.addAndPlay) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" @click="hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button :disabled="!address" @click="addServer(false)">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.addServer) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" :disabled="!address" @click="addServer(true)">
|
||||
<PlayIcon />
|
||||
{{ formatMessage(messages.addAndPlay) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { TrashIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
NewModal,
|
||||
@@ -106,18 +106,14 @@ defineExpose({ show, hide })
|
||||
|
||||
<template #actions>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="hide">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button :disabled="!isServer && !isSingleplayer" @click="confirm">
|
||||
<TrashIcon />
|
||||
{{ formatMessage(actionMessage) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" @click="hide">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="red" :disabled="!isServer && !isSingleplayer" @click="confirm">
|
||||
<TrashIcon />
|
||||
{{ formatMessage(actionMessage) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { SaveIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessage,
|
||||
injectNotificationManager,
|
||||
@@ -105,18 +105,14 @@ const titleMessage = defineMessage({
|
||||
<HideFromHomeOption v-model="hideFromHome" class="mt-3" />
|
||||
<template #actions>
|
||||
<div class="flex gap-2 justify-end">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="!address" @click="saveServer">
|
||||
<SaveIcon />
|
||||
{{ formatMessage(commonMessages.saveChangesButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" @click="hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" :disabled="!address" @click="saveServer">
|
||||
<SaveIcon />
|
||||
{{ formatMessage(commonMessages.saveChangesButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { ChevronRightIcon, SaveIcon, UndoIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
@@ -113,24 +113,18 @@ const messages = defineMessages({
|
||||
<HideFromHomeOption v-model="hideFromHome" class="mt-3" />
|
||||
</div>
|
||||
<div class="flex gap-2 mt-4">
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="saveWorld">
|
||||
<SaveIcon />
|
||||
{{ formatMessage(commonMessages.saveChangesButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button :disabled="removeIcon || !icon" @click="removeIcon = true">
|
||||
<UndoIcon />
|
||||
{{ formatMessage(messages.resetIcon) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="saveWorld">
|
||||
<SaveIcon />
|
||||
{{ formatMessage(commonMessages.saveChangesButton) }}
|
||||
</Button>
|
||||
<Button :disabled="removeIcon || !icon" @click="removeIcon = true">
|
||||
<UndoIcon />
|
||||
{{ formatMessage(messages.resetIcon) }}
|
||||
</Button>
|
||||
<Button @click="hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</ModalWrapper>
|
||||
</template>
|
||||
|
||||
@@ -260,6 +260,9 @@
|
||||
"app.browse.hide-added-servers": {
|
||||
"message": "Hide servers already added"
|
||||
},
|
||||
"app.browse.hide-installed-modpacks": {
|
||||
"message": "Hide already installed"
|
||||
},
|
||||
"app.browse.project-type.modpacks": {
|
||||
"message": "Modpacks"
|
||||
},
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
preferencesDiffer,
|
||||
provideBrowseManager,
|
||||
requestInstall,
|
||||
resolveInstallPlan,
|
||||
stripServerRuntimeInstallFilters,
|
||||
stripServerRuntimeInstallOverrides,
|
||||
useBrowseSearch,
|
||||
@@ -50,6 +51,7 @@ import { instance_listener } from '@/helpers/events.js'
|
||||
import {
|
||||
get as getInstance,
|
||||
get_installed_project_ids as getInstalledProjectIds,
|
||||
list as listInstances,
|
||||
} from '@/helpers/instance'
|
||||
import { get_loader_versions as getLoaderManifest } from '@/helpers/metadata'
|
||||
import { get as getSettings, set as setSettings } from '@/helpers/settings.ts'
|
||||
@@ -171,7 +173,7 @@ const instance: Ref<Instance | null> = ref(
|
||||
queryClient.getQueryData<Instance>(['instances', 'summary', initialInstanceId]) ?? null,
|
||||
)
|
||||
const installedProjectIds: Ref<string[] | null> = ref(null)
|
||||
const instanceHideInstalled = ref(false)
|
||||
const instanceHideInstalled = ref(route.query.ai === 'true')
|
||||
const newlyInstalled = ref<string[]>([])
|
||||
const hiddenInstanceProjectIds = ref<Set<string>>(new Set())
|
||||
const hiddenInstanceProjectIdsInitialized = ref(false)
|
||||
@@ -288,7 +290,17 @@ watchServerContextChanges()
|
||||
await initInstanceContext()
|
||||
|
||||
async function refreshInstalledProjectIds() {
|
||||
if (!route.query.i) return
|
||||
if (!route.query.i) {
|
||||
const instances = await listInstances().catch(handleError)
|
||||
if (!instances) return
|
||||
|
||||
const ids = instances
|
||||
.map((gameInstance) => gameInstance.link?.project_id)
|
||||
.filter((id): id is string => !!id)
|
||||
debugLog('installedInstanceProjectIds loaded', { count: ids.length })
|
||||
installedProjectIds.value = ids
|
||||
return
|
||||
}
|
||||
|
||||
if (route.query.from === 'worlds') {
|
||||
const worlds = await get_instance_worlds(route.query.i as string).catch(handleError)
|
||||
@@ -318,6 +330,7 @@ async function initInstanceContext() {
|
||||
queryFrom: route.query.from,
|
||||
})
|
||||
await initServerContext()
|
||||
await refreshInstalledProjectIds()
|
||||
|
||||
if (route.query.i) {
|
||||
instance.value = (await getInstance(route.query.i as string).catch(handleError)) ?? null
|
||||
@@ -327,8 +340,6 @@ async function initInstanceContext() {
|
||||
gameVersion: instance.value?.game_version,
|
||||
})
|
||||
|
||||
await refreshInstalledProjectIds()
|
||||
|
||||
if (instance.value?.link?.project_id) {
|
||||
debugLog('checking linked project for server status', instance.value.link.project_id)
|
||||
const projectV3 = await get_project_v3(
|
||||
@@ -341,13 +352,23 @@ async function initInstanceContext() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (route.query.ai && !(route.params.projectType === 'modpack')) {
|
||||
debugLog('setting instanceHideInstalled from query', route.query.ai)
|
||||
instanceHideInstalled.value = route.query.ai === 'true'
|
||||
}
|
||||
}
|
||||
|
||||
function setBrowseHideInstalledFlag(flag: 'hide_installed_modpacks', value: boolean) {
|
||||
themeStore.featureFlags[flag] = value
|
||||
getSettings()
|
||||
.then((settings) => {
|
||||
settings.feature_flags[flag] = value
|
||||
return setSettings(settings)
|
||||
})
|
||||
.catch(handleError)
|
||||
}
|
||||
|
||||
const hideInstalledModpacks = computed({
|
||||
get: () => themeStore.getFeatureFlag('hide_installed_modpacks'),
|
||||
set: (value: boolean) => setBrowseHideInstalledFlag('hide_installed_modpacks', value),
|
||||
})
|
||||
|
||||
const instanceFilters = computed(() => {
|
||||
const filters = []
|
||||
|
||||
@@ -367,11 +388,15 @@ const instanceFilters = computed(() => {
|
||||
if (isServerInstance.value) {
|
||||
filters.push({ type: 'environment', option: 'client' })
|
||||
}
|
||||
}
|
||||
|
||||
if (instanceHideInstalled.value && hiddenInstanceProjectIds.value.size > 0) {
|
||||
for (const id of hiddenInstanceProjectIds.value) {
|
||||
filters.push({ type: 'project_id', option: `project_id:${id}`, negative: true })
|
||||
}
|
||||
if (
|
||||
(instance.value || projectType.value === 'modpack') &&
|
||||
(projectType.value === 'modpack' ? hideInstalledModpacks.value : instanceHideInstalled.value) &&
|
||||
hiddenInstanceProjectIds.value.size > 0
|
||||
) {
|
||||
for (const id of hiddenInstanceProjectIds.value) {
|
||||
filters.push({ type: 'project_id', option: `project_id:${id}`, negative: true })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,6 +455,12 @@ const serverContextFilters = computed(() => {
|
||||
{ type: 'environment', option: 'client' },
|
||||
{ type: 'environment', option: 'server' },
|
||||
)
|
||||
|
||||
if (hideInstalledModpacks.value && hiddenInstanceProjectIds.value.size > 0) {
|
||||
for (const id of hiddenInstanceProjectIds.value) {
|
||||
filters.push({ type: 'project_id', option: `project_id:${id}`, negative: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serverHideInstalled.value && hiddenServerContentProjectIds.value.size > 0) {
|
||||
@@ -500,6 +531,10 @@ const messages = defineMessages({
|
||||
id: 'app.browse.hide-added-servers',
|
||||
defaultMessage: 'Hide servers already added',
|
||||
},
|
||||
hideInstalledModpacks: {
|
||||
id: 'app.browse.hide-installed-modpacks',
|
||||
defaultMessage: 'Hide already installed',
|
||||
},
|
||||
installingToServer: {
|
||||
id: 'app.browse.server.installing',
|
||||
defaultMessage: 'Installing',
|
||||
@@ -553,6 +588,7 @@ function resetInstanceContext() {
|
||||
hiddenInstanceProjectIdsInitialized.value = false
|
||||
isServerInstance.value = false
|
||||
browseBreadcrumb.reset()
|
||||
void refreshInstalledProjectIds()
|
||||
}
|
||||
|
||||
watch(
|
||||
@@ -763,6 +799,27 @@ async function chooseInstanceInstallVersion(
|
||||
return { versionId: selectedVersion.id }
|
||||
}
|
||||
|
||||
async function chooseFilterMatchingInstallVersion(
|
||||
project: Labrinth.Search.v3.ResultSearchProject,
|
||||
projectTypeValue: string,
|
||||
) {
|
||||
const plan = await resolveInstallPlan({
|
||||
project: {
|
||||
project_id: project.project_id,
|
||||
title: project.title,
|
||||
icon_url: project.icon_url,
|
||||
},
|
||||
contentType: projectTypeValue as BrowseInstallContentType,
|
||||
selectedFilters: searchState.currentFilters.value,
|
||||
providedFilters: combinedProvidedFilters.value,
|
||||
overriddenProvidedFilterTypes: searchState.overriddenProvidedFilterTypes.value,
|
||||
targetPreferences: {},
|
||||
getProjectVersions: getInstallProjectVersions,
|
||||
})
|
||||
|
||||
return { versionId: plan.versionId }
|
||||
}
|
||||
|
||||
function getCardActions(
|
||||
result: Labrinth.Search.v3.ResultSearchProject,
|
||||
currentProjectType: string,
|
||||
@@ -781,6 +838,7 @@ function getCardActions(
|
||||
serverContentProjectIds.value.has(projectResult.project_id || '') ||
|
||||
serverContextServerData.value?.upstream?.project_id === projectResult.project_id
|
||||
const isInstalling = installingProjectIds.value.has(projectResult.project_id)
|
||||
const showAsInstalled = isInstalled && currentProjectType !== 'modpack'
|
||||
|
||||
if (
|
||||
isServerContext.value &&
|
||||
@@ -790,7 +848,7 @@ function getCardActions(
|
||||
const isInstallingSelection = isInstallingQueuedServerInstalls.value
|
||||
const validatingInstall =
|
||||
isInstalling && currentProjectType !== 'modpack' && !isInstallingSelection
|
||||
const installLabel = isInstalled
|
||||
const installLabel = showAsInstalled
|
||||
? commonMessages.installedLabel
|
||||
: isQueued
|
||||
? isInstalling || isInstallingSelection
|
||||
@@ -810,11 +868,11 @@ function getCardActions(
|
||||
icon:
|
||||
isInstalling || isInstallingSelection
|
||||
? SpinnerIcon
|
||||
: isQueued || isInstalled
|
||||
: isQueued || showAsInstalled
|
||||
? CheckIcon
|
||||
: PlusIcon,
|
||||
iconClass: isInstalling || isInstallingSelection ? 'animate-spin' : undefined,
|
||||
disabled: isInstalled || isInstalling || isInstallingSelection,
|
||||
disabled: showAsInstalled || isInstalling || isInstallingSelection,
|
||||
color: isQueued && !isInstalling && !isInstallingSelection ? 'green' : 'brand',
|
||||
type: 'outlined',
|
||||
onClick: async () => {
|
||||
@@ -875,15 +933,15 @@ function getCardActions(
|
||||
label: formatMessage(
|
||||
isInstalling
|
||||
? messages.installingToServer
|
||||
: isInstalled
|
||||
: showAsInstalled
|
||||
? commonMessages.installedLabel
|
||||
: shouldUseInstallIcon
|
||||
? commonMessages.installButton
|
||||
: messages.addToAnInstance,
|
||||
),
|
||||
icon: isInstalling ? SpinnerIcon : isInstalled ? CheckIcon : PlusIcon,
|
||||
icon: isInstalling ? SpinnerIcon : showAsInstalled ? CheckIcon : PlusIcon,
|
||||
iconClass: isInstalling ? 'animate-spin' : undefined,
|
||||
disabled: isInstalled || isInstalling,
|
||||
disabled: showAsInstalled || isInstalling,
|
||||
color: 'brand',
|
||||
type: 'outlined',
|
||||
onClick: async () => {
|
||||
@@ -891,7 +949,9 @@ function getCardActions(
|
||||
try {
|
||||
const selectedInstall = instance.value
|
||||
? await chooseInstanceInstallVersion(projectResult, currentProjectType)
|
||||
: { versionId: null as string | null }
|
||||
: isModpack
|
||||
? await chooseFilterMatchingInstallVersion(projectResult, currentProjectType)
|
||||
: { versionId: null as string | null }
|
||||
if (selectedInstall === null) {
|
||||
setProjectInstalling(projectResult.project_id, false)
|
||||
return
|
||||
@@ -994,10 +1054,11 @@ async function search(requestParams: string) {
|
||||
...hit,
|
||||
}
|
||||
|
||||
if (instance.value || isServerContext.value) {
|
||||
const installedIds = instance.value
|
||||
? new Set([...newlyInstalled.value, ...(installedProjectIds.value ?? [])])
|
||||
: serverContentProjectIds.value
|
||||
if (instance.value || isServerContext.value || projectType.value === 'modpack') {
|
||||
const installedIds =
|
||||
isServerContext.value && projectType.value !== 'modpack'
|
||||
? serverContentProjectIds.value
|
||||
: new Set([...newlyInstalled.value, ...(installedProjectIds.value ?? [])])
|
||||
mapped.installed = installedIds.has(hit.project_id)
|
||||
}
|
||||
|
||||
@@ -1057,9 +1118,9 @@ watch(
|
||||
() => projectType.value,
|
||||
],
|
||||
() => {
|
||||
if (isServerContext.value) {
|
||||
if (isServerContext.value && projectType.value !== 'modpack') {
|
||||
syncHiddenServerContentProjectIds()
|
||||
} else if (instance.value) {
|
||||
} else if (instance.value || projectType.value === 'modpack') {
|
||||
syncHiddenInstanceProjectIds()
|
||||
}
|
||||
},
|
||||
@@ -1091,6 +1152,18 @@ let unlistenInstances: UnlistenFn | null = null
|
||||
|
||||
onMounted(() => {
|
||||
instance_listener(async (event: { event: string; instance_id: string }) => {
|
||||
if (event.event === 'added' || event.event === 'created' || event.event === 'removed') {
|
||||
if (!route.query.i) {
|
||||
await refreshInstalledProjectIds()
|
||||
if (projectType.value === 'modpack') {
|
||||
if (event.event === 'removed') {
|
||||
syncHiddenInstanceProjectIds()
|
||||
}
|
||||
await searchState.refreshSearch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (instance.value && event.instance_id === instance.value.id && event.event === 'synced') {
|
||||
await refreshInstalledProjectIds()
|
||||
await searchState.refreshSearch()
|
||||
@@ -1156,8 +1229,17 @@ provideBrowseManager({
|
||||
installContext,
|
||||
providedFilters: combinedProvidedFilters,
|
||||
hideInstalled: computed({
|
||||
get: () => (isServerContext.value ? serverHideInstalled.value : instanceHideInstalled.value),
|
||||
get: () => {
|
||||
if (projectType.value === 'modpack') return hideInstalledModpacks.value
|
||||
if (isServerContext.value) return serverHideInstalled.value
|
||||
return instanceHideInstalled.value
|
||||
},
|
||||
set: (val: boolean) => {
|
||||
if (projectType.value === 'modpack') {
|
||||
hideInstalledModpacks.value = val
|
||||
if (val) syncHiddenInstanceProjectIds()
|
||||
return
|
||||
}
|
||||
if (isServerContext.value) {
|
||||
serverHideInstalled.value = val
|
||||
if (val) syncHiddenServerContentProjectIds()
|
||||
@@ -1168,11 +1250,18 @@ provideBrowseManager({
|
||||
},
|
||||
}),
|
||||
showHideInstalled: computed(
|
||||
() => (isServerContext.value && projectType.value !== 'modpack') || !!instance.value,
|
||||
() =>
|
||||
projectType.value === 'modpack' ||
|
||||
(isServerContext.value && projectType.value !== 'modpack') ||
|
||||
!!instance.value,
|
||||
),
|
||||
hideInstalledLabel: computed(() =>
|
||||
formatMessage(
|
||||
isFromWorlds.value ? messages.hideAddedServers : commonMessages.hideInstalledContentLabel,
|
||||
isFromWorlds.value
|
||||
? messages.hideAddedServers
|
||||
: projectType.value === 'modpack'
|
||||
? messages.hideInstalledModpacks
|
||||
: commonMessages.hideInstalledContentLabel,
|
||||
),
|
||||
),
|
||||
hideSelected: hideSelectedServerInstalls,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import {
|
||||
CheckIcon,
|
||||
EditIcon,
|
||||
@@ -10,7 +11,6 @@ import {
|
||||
SpinnerIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
ConfirmModal,
|
||||
defineMessages,
|
||||
@@ -1180,8 +1180,7 @@ await loadSkins()
|
||||
>Ears</router-link
|
||||
>
|
||||
</div>
|
||||
<ButtonStyled type="outlined">
|
||||
<button
|
||||
<Button type="outlined"
|
||||
class="ears-feature-toggle-button !h-10 !rounded-[14px] !px-4 shadow-md"
|
||||
:aria-pressed="earsFeaturesEnabled"
|
||||
:aria-label="
|
||||
@@ -1193,15 +1192,14 @@ await loadSkins()
|
||||
"
|
||||
@click="earsFeaturesEnabled = !earsFeaturesEnabled"
|
||||
>
|
||||
{{
|
||||
formatMessage(
|
||||
earsFeaturesEnabled
|
||||
? messages.toggleEarsFeaturesOff
|
||||
: messages.toggleEarsFeaturesOn,
|
||||
)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
{{
|
||||
formatMessage(
|
||||
earsFeaturesEnabled
|
||||
? messages.toggleEarsFeaturesOff
|
||||
: messages.toggleEarsFeaturesOn,
|
||||
)
|
||||
}}
|
||||
</Button>
|
||||
<Toggle
|
||||
v-model="earsFeaturesEnabled"
|
||||
v-tooltip="
|
||||
@@ -1278,13 +1276,11 @@ await loadSkins()
|
||||
<p class="text-lg m-0">
|
||||
{{ formatMessage(messages.signInDescription) }}
|
||||
</p>
|
||||
<ButtonStyled v-show="accountsCard" color="brand" :disabled="accountsCard.loginDisabled">
|
||||
<button :disabled="accountsCard.loginDisabled" @click="login">
|
||||
<LogInIcon v-if="!accountsCard.loginDisabled" />
|
||||
<SpinnerIcon v-else class="animate-spin" />
|
||||
{{ formatMessage(messages.signInButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" v-show="accountsCard" :disabled="accountsCard.loginDisabled" @click="login">
|
||||
<LogInIcon v-if="!accountsCard.loginDisabled" />
|
||||
<SpinnerIcon v-else class="animate-spin" />
|
||||
{{ formatMessage(messages.signInButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1310,14 +1310,16 @@ function getOverflowOptions(item: ContentItem): OverflowMenuOption[] {
|
||||
const options: OverflowMenuOption[] = []
|
||||
|
||||
options.push({
|
||||
id: formatMessage(commonMessages.showFileButton),
|
||||
id: 'show-file',
|
||||
label: formatMessage(commonMessages.showFileButton),
|
||||
icon: FolderOpenIcon,
|
||||
action: () => highlightModInInstance(props.instance.id, item.file_path),
|
||||
})
|
||||
|
||||
if (item.project?.slug) {
|
||||
options.push({
|
||||
id: formatMessage(commonMessages.copyLinkButton),
|
||||
id: 'copy-link',
|
||||
label: formatMessage(commonMessages.copyLinkButton),
|
||||
icon: ClipboardCopyIcon,
|
||||
action: async () => {
|
||||
await navigator.clipboard.writeText(
|
||||
|
||||
@@ -35,23 +35,19 @@
|
||||
"
|
||||
/>
|
||||
<div class="flex gap-2">
|
||||
<ButtonStyled type="outlined">
|
||||
<button class="!h-10" @click="addServerModal?.show()">
|
||||
<PlusIcon class="size-5" />
|
||||
{{ formatMessage(messages.addServer) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
<Button type="outlined" class="!h-10" @click="addServerModal?.show()">
|
||||
<PlusIcon class="size-5" />
|
||||
{{ formatMessage(messages.addServer) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand"
|
||||
class="!h-10 flex items-center gap-2"
|
||||
@click="
|
||||
router.push({ path: '/browse/server', query: { i: instance.id, from: 'worlds' } })
|
||||
"
|
||||
>
|
||||
<CompassIcon class="size-5" />
|
||||
<span>{{ formatMessage(messages.browseServers) }}</span>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<CompassIcon class="size-5" />
|
||||
<span>{{ formatMessage(messages.browseServers) }}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||
@@ -72,12 +68,10 @@
|
||||
{{ option.label }}
|
||||
</button>
|
||||
</div>
|
||||
<ButtonStyled type="transparent" hover-color-fill="none">
|
||||
<button :disabled="refreshingAll" @click="refreshAllWorlds">
|
||||
<RefreshCwIcon :class="refreshingAll ? 'animate-spin' : ''" />
|
||||
{{ formatMessage(commonMessages.refreshButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" :disabled="refreshingAll" @click="refreshAllWorlds" class="hover:!bg-transparent focus-visible:!bg-transparent">
|
||||
<RefreshCwIcon :class="refreshingAll ? 'animate-spin' : ''" />
|
||||
{{ formatMessage(commonMessages.refreshButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex flex-col w-full gap-2">
|
||||
<WorldItem
|
||||
@@ -123,31 +117,27 @@
|
||||
:description="formatMessage(messages.noWorldsDescription)"
|
||||
>
|
||||
<template #actions>
|
||||
<ButtonStyled type="outlined">
|
||||
<button class="!h-10" @click="addServerModal?.show()">
|
||||
<PlusIcon class="size-5" />
|
||||
{{ formatMessage(messages.addServer) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
<Button type="outlined" class="!h-10" @click="addServerModal?.show()">
|
||||
<PlusIcon class="size-5" />
|
||||
{{ formatMessage(messages.addServer) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand"
|
||||
class="!h-10 flex items-center gap-2"
|
||||
@click="
|
||||
router.push({ path: '/browse/server', query: { i: instance.id, from: 'worlds' } })
|
||||
"
|
||||
>
|
||||
<CompassIcon class="size-5" />
|
||||
<span>{{ formatMessage(messages.browseServers) }}</span>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<CompassIcon class="size-5" />
|
||||
<span>{{ formatMessage(messages.browseServers) }}</span>
|
||||
</Button>
|
||||
</template>
|
||||
</EmptyState>
|
||||
</ReadyTransition>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { CompassIcon, FilterIcon, PlusIcon, RefreshCwIcon, SearchIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
EmptyState,
|
||||
|
||||
@@ -109,11 +109,9 @@
|
||||
</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<ButtonStyled color="brand"
|
||||
><button class="!h-10" @click="signInToShare">
|
||||
<LogInIcon aria-hidden="true" />{{ formatMessage(lockedActionButton) }}
|
||||
</button></ButtonStyled
|
||||
>
|
||||
<Button type="colored" color="brand" class="!h-10" @click="signInToShare">
|
||||
<LogInIcon aria-hidden="true" />{{ formatMessage(lockedActionButton) }}
|
||||
</Button>
|
||||
</template>
|
||||
</SharedInstanceShareEmptyState>
|
||||
|
||||
@@ -123,21 +121,19 @@
|
||||
:description="formatMessage(messages.noFriendsInvitedDescription)"
|
||||
>
|
||||
<template #actions>
|
||||
<ButtonStyled color="brand"
|
||||
><button
|
||||
<Button type="colored" color="brand"
|
||||
class="!h-10"
|
||||
:disabled="inviteLink.pending.value || !hasRemainingUserSlots"
|
||||
@click="showInvitePlayers($event)"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="inviteLink.pending.value"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/><UserPlusIcon v-else aria-hidden="true" />{{
|
||||
formatMessage(messages.inviteFriendsButton)
|
||||
}}
|
||||
</button></ButtonStyled
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="inviteLink.pending.value"
|
||||
class="animate-spin"
|
||||
aria-hidden="true"
|
||||
/><UserPlusIcon v-else aria-hidden="true" />{{
|
||||
formatMessage(messages.inviteFriendsButton)
|
||||
}}
|
||||
</Button>
|
||||
</template>
|
||||
</SharedInstanceShareEmptyState>
|
||||
</div>
|
||||
@@ -145,17 +141,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { LogInIcon, SpinnerIcon, UserPlusIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
ConfirmUnlinkModal,
|
||||
defineMessages,
|
||||
injectAuth,
|
||||
type InvitePlayersInvitePayload,
|
||||
InvitePlayersModal,
|
||||
type InvitePlayersUser,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { Avatar, ConfirmUnlinkModal, defineMessages, injectAuth, type InvitePlayersInvitePayload, InvitePlayersModal, type InvitePlayersUser, useVIntl, Button } from '@modrinth/ui'
|
||||
import { useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { computed, ref, toRef, watch } from 'vue'
|
||||
|
||||
|
||||
@@ -11,28 +11,24 @@
|
||||
clearable
|
||||
/>
|
||||
<template v-if="!actionsLocked">
|
||||
<ButtonStyled type="outlined">
|
||||
<button
|
||||
<Button type="outlined"
|
||||
class="flex !h-10 shrink-0 items-center gap-2 !border"
|
||||
:disabled="pushUpdateDisabled"
|
||||
@click="emit('push-update', $event)"
|
||||
>
|
||||
<SpinnerIcon v-if="pushUpdatePending" class="animate-spin" aria-hidden="true" />
|
||||
<UploadIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.pushUpdate) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
<SpinnerIcon v-if="pushUpdatePending" class="animate-spin" aria-hidden="true" />
|
||||
<UploadIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.pushUpdate) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand"
|
||||
class="flex !h-10 shrink-0 items-center gap-2"
|
||||
:disabled="invitePending || inviteDisabled"
|
||||
@click="emit('invite', $event)"
|
||||
>
|
||||
<SpinnerIcon v-if="invitePending" class="animate-spin" aria-hidden="true" />
|
||||
<UserPlusIcon v-else aria-hidden="true" />
|
||||
Invite friends
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<SpinnerIcon v-if="invitePending" class="animate-spin" aria-hidden="true" />
|
||||
<UserPlusIcon v-else aria-hidden="true" />
|
||||
Invite friends
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="hasMultipleMethods" class="flex flex-wrap items-center gap-1.5">
|
||||
@@ -120,15 +116,12 @@
|
||||
</template>
|
||||
<template #cell-actions="{ row }">
|
||||
<div v-if="!actionsLocked" class="flex items-center justify-end">
|
||||
<ButtonStyled circular type="transparent"
|
||||
><button
|
||||
<IconButton type="quiet" :label="`Revoke access for ${row.username}`"
|
||||
v-tooltip="'Revoke access'"
|
||||
:aria-label="`Revoke access for ${row.username}`"
|
||||
class="text-secondary hover:!filter-none hover:text-red focus-visible:!filter-none"
|
||||
@click="emit('remove', row)"
|
||||
>
|
||||
<XIcon aria-hidden="true" /></button
|
||||
></ButtonStyled>
|
||||
<XIcon aria-hidden="true" /></IconButton>
|
||||
</div>
|
||||
</template>
|
||||
</Table>
|
||||
@@ -136,6 +129,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, IconButton } from '@modrinth/ui'
|
||||
import {
|
||||
FilterIcon,
|
||||
LinkIcon,
|
||||
@@ -145,20 +139,7 @@ import {
|
||||
UserPlusIcon,
|
||||
XIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
AutoLink,
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
type SortDirection,
|
||||
StyledInput,
|
||||
Table,
|
||||
type TableColumn,
|
||||
truncatedTooltip,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { AutoLink, Avatar, defineMessages, type SortDirection, StyledInput, Table, type TableColumn, truncatedTooltip, useFormatDateTime, useRelativeTime, useVIntl } from '@modrinth/ui'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
import {
|
||||
|
||||
@@ -32,16 +32,12 @@
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex justify-end gap-2 pt-1">
|
||||
<ButtonStyled type="outlined"
|
||||
><button class="!border !border-surface-5" @click="modal?.hide()">
|
||||
<XIcon aria-hidden="true" />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button></ButtonStyled
|
||||
>
|
||||
<ButtonStyled color="orange"
|
||||
><button :disabled="!row" @click="confirm">
|
||||
<UserXIcon aria-hidden="true" />{{ formatMessage(messages.removeButton) }}
|
||||
</button></ButtonStyled
|
||||
>
|
||||
<Button type="outlined" class="!border !border-surface-5" @click="modal?.hide()">
|
||||
<XIcon aria-hidden="true" />{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="orange" :disabled="!row" @click="confirm">
|
||||
<UserXIcon aria-hidden="true" />{{ formatMessage(messages.removeButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</NewModal>
|
||||
@@ -49,15 +45,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { UserXIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
NewModal,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { Admonition, Avatar, commonMessages, defineMessages, NewModal, useVIntl, Button } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import { methodLabels, type ShareRow } from './shared-instance-share-types'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { LibraryIcon, PlusIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, injectNotificationManager, NavTabs } from '@modrinth/ui'
|
||||
import { injectNotificationManager, NavTabs } from '@modrinth/ui'
|
||||
import { inject, onUnmounted, ref, shallowRef } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
@@ -53,12 +54,10 @@ onUnmounted(() => {
|
||||
{ label: 'Saved', href: `/library/saved`, shown: false },
|
||||
]"
|
||||
/>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="offline" @click="showCreationModal?.()">
|
||||
<PlusIcon />
|
||||
New instance
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" :disabled="offline" @click="showCreationModal?.()">
|
||||
<PlusIcon />
|
||||
New instance
|
||||
</Button>
|
||||
</div>
|
||||
<template v-if="instances && instances.length > 0">
|
||||
<RouterView v-if="route.path.startsWith('/library')" :instances="instances" />
|
||||
@@ -68,12 +67,10 @@ onUnmounted(() => {
|
||||
<NewInstanceImage />
|
||||
</div>
|
||||
<h3>No instances found</h3>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="offline" @click="showCreationModal?.()">
|
||||
<PlusIcon />
|
||||
Create new instance
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" :disabled="offline" @click="showCreationModal?.()">
|
||||
<PlusIcon />
|
||||
Create new instance
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -39,14 +39,11 @@
|
||||
</div>
|
||||
<div class="controls">
|
||||
<div class="buttons">
|
||||
<ButtonStyled circular>
|
||||
<button class="close" @click="hideImage">
|
||||
<XIcon aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular>
|
||||
<a
|
||||
class="open btn icon-only"
|
||||
<IconButton label="Close" class="close" @click="hideImage">
|
||||
<XIcon aria-hidden="true" />
|
||||
</IconButton>
|
||||
<ButtonLink
|
||||
class="open btn icon-only !w-9 !px-0 !rounded-full"
|
||||
target="_blank"
|
||||
:href="
|
||||
expandedGalleryItem.raw_url
|
||||
@@ -54,25 +51,18 @@
|
||||
: 'https://cdn.modrinth.com/placeholder-banner.svg'
|
||||
"
|
||||
>
|
||||
<ExternalIcon aria-hidden="true" />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular>
|
||||
<button @click="zoomedIn = !zoomedIn">
|
||||
<ExpandIcon v-if="!zoomedIn" aria-hidden="true" />
|
||||
<ContractIcon v-else aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="filteredGallery.length > 1" circular>
|
||||
<button class="previous" @click="previousImage()">
|
||||
<LeftArrowIcon aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="filteredGallery.length > 1" circular>
|
||||
<button class="next" @click="nextImage()">
|
||||
<RightArrowIcon aria-hidden="true" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ExternalIcon aria-hidden="true" />
|
||||
</ButtonLink>
|
||||
<IconButton label="Toggle zoom" @click="zoomedIn = !zoomedIn">
|
||||
<ExpandIcon v-if="!zoomedIn" aria-hidden="true" />
|
||||
<ContractIcon v-else aria-hidden="true" />
|
||||
</IconButton>
|
||||
<IconButton label="Previous image" v-if="filteredGallery.length > 1" class="previous" @click="previousImage()">
|
||||
<LeftArrowIcon aria-hidden="true" />
|
||||
</IconButton>
|
||||
<IconButton label="Next image" v-if="filteredGallery.length > 1" class="next" @click="nextImage()">
|
||||
<RightArrowIcon aria-hidden="true" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -81,6 +71,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ButtonLink, IconButton } from '@modrinth/ui'
|
||||
import {
|
||||
CalendarIcon,
|
||||
ContractIcon,
|
||||
@@ -90,7 +81,7 @@ import {
|
||||
RightArrowIcon,
|
||||
XIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { ButtonStyled, Card, useFormatDateTime } from '@modrinth/ui'
|
||||
import { Card, useFormatDateTime } from '@modrinth/ui'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
import { hide_ads_window, show_ads_window } from '@/helpers/ads.js'
|
||||
|
||||
@@ -70,87 +70,66 @@
|
||||
>
|
||||
<template #actions>
|
||||
<template v-if="isServerProject">
|
||||
<ButtonStyled v-if="serverPlaying" color="red" size="large">
|
||||
<button type="button" @click="handleStopServer">
|
||||
<StopCircleIcon />
|
||||
{{ formatMessage(commonMessages.stopButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else color="brand" size="large">
|
||||
<button type="button" :disabled="serverInstallLoading" @click="handleClickPlay">
|
||||
<PlayIcon />
|
||||
{{
|
||||
serverInstallLoading
|
||||
? formatMessage(commonMessages.installingLabel)
|
||||
: formatMessage(commonMessages.playButton)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular size="large">
|
||||
<button
|
||||
<Button type="colored" color="red" size="xl" v-if="serverPlaying" native-type="button" @click="handleStopServer">
|
||||
<StopCircleIcon />
|
||||
{{ formatMessage(commonMessages.stopButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" size="xl" v-else native-type="button" :disabled="serverInstallLoading" @click="handleClickPlay">
|
||||
<PlayIcon />
|
||||
{{
|
||||
serverInstallLoading
|
||||
? formatMessage(commonMessages.installingLabel)
|
||||
: formatMessage(commonMessages.playButton)
|
||||
}}
|
||||
</Button>
|
||||
<IconButton size="xl" :label="formatMessage(commonMessages.addServerToInstanceButton)"
|
||||
v-tooltip="formatMessage(commonMessages.addServerToInstanceButton)"
|
||||
type="button"
|
||||
:aria-label="formatMessage(commonMessages.addServerToInstanceButton)"
|
||||
native-type="button"
|
||||
@click="handleAddServerToInstance"
|
||||
>
|
||||
<PlusIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular size="large" type="transparent">
|
||||
<TeleportOverflowMenu
|
||||
<PlusIcon />
|
||||
</IconButton>
|
||||
<TeleportOverflowMenu type="quiet" size="xl" label="More options"
|
||||
:options="serverProjectHeaderMoreActions"
|
||||
tooltip="More options"
|
||||
aria-label="More options"
|
||||
>
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</ButtonStyled>
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ButtonStyled v-if="showSwitchVersion && onVersionsPage" size="large">
|
||||
<button v-tooltip="formatMessage(messages.alreadyInstalled)" type="button" disabled>
|
||||
<CheckIcon />
|
||||
{{ formatMessage(commonMessages.installedLabel) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else-if="showSwitchVersion" size="large">
|
||||
<button type="button" @click="goToVersions">
|
||||
<SwapIcon />
|
||||
{{ formatMessage(messages.switchVersion) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else color="brand" size="large">
|
||||
<button
|
||||
<Button size="xl" v-if="showSwitchVersion && onVersionsPage" v-tooltip="formatMessage(messages.alreadyInstalled)" native-type="button" disabled>
|
||||
<CheckIcon />
|
||||
{{ formatMessage(commonMessages.installedLabel) }}
|
||||
</Button>
|
||||
<Button size="xl" v-else-if="showSwitchVersion" native-type="button" @click="goToVersions">
|
||||
<SwapIcon />
|
||||
{{ formatMessage(messages.switchVersion) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" size="xl" v-else
|
||||
v-tooltip="
|
||||
installButtonInstalled ? formatMessage(messages.alreadyInstalled) : undefined
|
||||
"
|
||||
type="button"
|
||||
native-type="button"
|
||||
:disabled="installButtonDisabled"
|
||||
@click="install(null)"
|
||||
>
|
||||
<component :is="installButtonIcon" :class="installButtonIconClass" />
|
||||
{{
|
||||
installButtonInstalled
|
||||
? formatMessage(commonMessages.installedLabel)
|
||||
: installButtonValidating
|
||||
? formatMessage(commonMessages.validatingLabel)
|
||||
: installButtonLoading
|
||||
? formatMessage(commonMessages.installingLabel)
|
||||
: serverProjectSelected
|
||||
? formatMessage(commonMessages.selectedLabel)
|
||||
: formatMessage(commonMessages.installButton)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular size="large" type="transparent">
|
||||
<TeleportOverflowMenu
|
||||
<component :is="installButtonIcon" :class="installButtonIconClass" />
|
||||
{{
|
||||
installButtonInstalled
|
||||
? formatMessage(commonMessages.installedLabel)
|
||||
: installButtonValidating
|
||||
? formatMessage(commonMessages.validatingLabel)
|
||||
: installButtonLoading
|
||||
? formatMessage(commonMessages.installingLabel)
|
||||
: serverProjectSelected
|
||||
? formatMessage(commonMessages.selectedLabel)
|
||||
: formatMessage(commonMessages.installButton)
|
||||
}}
|
||||
</Button>
|
||||
<TeleportOverflowMenu type="quiet" size="xl" label="More options"
|
||||
:options="projectHeaderMoreActions"
|
||||
tooltip="More options"
|
||||
aria-label="More options"
|
||||
>
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</ButtonStyled>
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</template>
|
||||
</template>
|
||||
</ProjectPageHeader>
|
||||
@@ -224,6 +203,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Button, IconButton, TeleportOverflowMenu } from '@modrinth/ui'
|
||||
import {
|
||||
BookmarkIcon,
|
||||
CheckIcon,
|
||||
@@ -241,7 +221,6 @@ import {
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
BrowseInstallHeader,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
CreationFlowModal,
|
||||
defineMessages,
|
||||
@@ -258,7 +237,6 @@ import {
|
||||
ProjectSidebarTags,
|
||||
requestInstall,
|
||||
SelectedProjectsFloatingBar,
|
||||
TeleportOverflowMenu,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { useQueryClient } from '@tanstack/vue-query'
|
||||
@@ -571,13 +549,13 @@ const serverProjectHeaderMoreActions = computed(() => [
|
||||
action: openProjectInBrowser,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
id: 'report',
|
||||
label: formatMessage(commonMessages.reportButton),
|
||||
icon: ReportIcon,
|
||||
color: 'red',
|
||||
tone: 'red',
|
||||
action: reportProject,
|
||||
},
|
||||
])
|
||||
@@ -605,13 +583,13 @@ const projectHeaderMoreActions = computed(() => [
|
||||
action: openProjectInBrowser,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
id: 'report',
|
||||
label: formatMessage(commonMessages.reportButton),
|
||||
icon: ReportIcon,
|
||||
color: 'red',
|
||||
tone: 'red',
|
||||
action: reportProject,
|
||||
},
|
||||
])
|
||||
|
||||
@@ -15,68 +15,65 @@
|
||||
:dependency-link-creator="createDependencyLink"
|
||||
>
|
||||
<template #headerActions>
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
<Button type="colored" color="brand"
|
||||
:disabled="installing || (installed && installedVersion === version.id)"
|
||||
@click="() => version && install(version.id)"
|
||||
>
|
||||
<DownloadIcon v-if="!installed" />
|
||||
<SwapIcon v-else-if="installedVersion !== version.id" />
|
||||
<CheckIcon v-else />
|
||||
{{
|
||||
installing
|
||||
? formatMessage(messages.installing)
|
||||
: installed && installedVersion === version.id
|
||||
? formatMessage(commonMessages.installedLabel)
|
||||
: installed
|
||||
? formatMessage(commonMessages.switchToVersionButton)
|
||||
: formatMessage(commonMessages.installButton)
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled type="outlined" circular>
|
||||
<OverflowMenu
|
||||
<DownloadIcon v-if="!installed" />
|
||||
<SwapIcon v-else-if="installedVersion !== version.id" />
|
||||
<CheckIcon v-else />
|
||||
{{
|
||||
installing
|
||||
? formatMessage(messages.installing)
|
||||
: installed && installedVersion === version.id
|
||||
? formatMessage(commonMessages.installedLabel)
|
||||
: installed
|
||||
? formatMessage(commonMessages.switchToVersionButton)
|
||||
: formatMessage(commonMessages.installButton)
|
||||
}}
|
||||
</Button>
|
||||
<TeleportOverflowMenu type="outlined" label="More options"
|
||||
v-tooltip="formatMessage(commonMessages.moreOptionsButton)"
|
||||
:options="[
|
||||
{
|
||||
id: 'open-in-browser',
|
||||
link: `https://modrinth.com/${project.project_type}/${project.slug}/version/${version.id}`,
|
||||
external: true,
|
||||
label: formatMessage(commonMessages.openInBrowserButton),
|
||||
type: 'link',
|
||||
href: `https://modrinth.com/${project.project_type}/${project.slug}/version/${version.id}`,
|
||||
target: '_blank',
|
||||
},
|
||||
{
|
||||
id: 'report',
|
||||
color: 'red',
|
||||
hoverFilled: true,
|
||||
link: `https://modrinth.com/report?item=version&itemID=${version.id}`,
|
||||
external: true,
|
||||
label: formatMessage(commonMessages.reportButton),
|
||||
type: 'link',
|
||||
tone: 'red',
|
||||
href: `https://modrinth.com/report?item=version&itemID=${version.id}`,
|
||||
target: '_blank',
|
||||
},
|
||||
]"
|
||||
aria-label="More options"
|
||||
>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #open-in-browser>
|
||||
<ExternalIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.openInBrowserButton) }}
|
||||
</template>
|
||||
<template #report>
|
||||
<ReportIcon aria-hidden="true" /> {{ formatMessage(commonMessages.reportButton) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #open-in-browser>
|
||||
<ExternalIcon aria-hidden="true" />
|
||||
{{ formatMessage(commonMessages.openInBrowserButton) }}
|
||||
</template>
|
||||
<template #report>
|
||||
<ReportIcon aria-hidden="true" /> {{ formatMessage(commonMessages.reportButton) }}
|
||||
</template>
|
||||
</TeleportOverflowMenu>
|
||||
</template>
|
||||
<template #supplementaryResourceActions="{ file }">
|
||||
<ButtonStyled>
|
||||
<a :href="file.url" :download="file.filename" target="_blank">
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.downloadInBrowser) }}
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonLink :href="file.url" :download="file.filename" target="_blank">
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
{{ formatMessage(messages.downloadInBrowser) }}
|
||||
</ButtonLink>
|
||||
</template>
|
||||
</VersionPage>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, ButtonLink, TeleportOverflowMenu } from '@modrinth/ui'
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import {
|
||||
CheckIcon,
|
||||
@@ -88,11 +85,9 @@ import {
|
||||
VersionIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
type DependencyContext,
|
||||
OverflowMenu,
|
||||
useVIntl,
|
||||
VersionPage,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
:version-link="(version) => buildProjectHref(`/project/${project.id}/version/${version.id}`)"
|
||||
>
|
||||
<template #actions="{ version }">
|
||||
<ButtonStyled
|
||||
circular
|
||||
type="transparent"
|
||||
:color="installed && version.id === installedVersion ? 'standard' : 'green'"
|
||||
>
|
||||
<button
|
||||
<IconButton
|
||||
type="quiet"
|
||||
:color="installed && version.id === installedVersion ? undefined : 'green'"
|
||||
:label="
|
||||
!installed
|
||||
? formatMessage(commonMessages.installButton)
|
||||
: version.id !== installedVersion
|
||||
? formatMessage(commonMessages.switchToVersionButton)
|
||||
: formatMessage(messages.alreadyInstalled)
|
||||
"
|
||||
v-tooltip="
|
||||
!installed
|
||||
? formatMessage(commonMessages.installButton)
|
||||
@@ -25,60 +29,33 @@
|
||||
:disabled="installing || (installed && version.id === installedVersion)"
|
||||
@click.stop="() => install(version.id)"
|
||||
>
|
||||
<DownloadIcon v-if="!installed" />
|
||||
<SwapIcon v-else-if="installed && version.id !== installedVersion" />
|
||||
<CheckIcon v-else />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular type="transparent">
|
||||
<OverflowMenu
|
||||
v-if="false"
|
||||
:options="[
|
||||
{
|
||||
id: 'install-elsewhere',
|
||||
action: () => {},
|
||||
shown: false && !!instance,
|
||||
color: 'primary',
|
||||
hoverFilled: true,
|
||||
},
|
||||
{
|
||||
id: 'open-in-browser',
|
||||
link: `https://modrinth.com/${project.project_type}/${project.slug}/version/${version.id}`,
|
||||
},
|
||||
]"
|
||||
aria-label="More options"
|
||||
>
|
||||
<MoreVerticalIcon aria-hidden="true" />
|
||||
<template #install-elsewhere>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
Add to another instance
|
||||
</template>
|
||||
<template #open-in-browser>
|
||||
<ExternalIcon /> {{ formatMessage(commonMessages.openInBrowserButton) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
<a
|
||||
v-else
|
||||
v-tooltip="formatMessage(commonMessages.openInBrowserButton)"
|
||||
:href="`https://modrinth.com/${project.project_type}/${project.slug}/version/${version.id}`"
|
||||
target="_blank"
|
||||
>
|
||||
<ExternalIcon />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<DownloadIcon v-if="!installed" />
|
||||
<SwapIcon v-else-if="installed && version.id !== installedVersion" />
|
||||
<CheckIcon v-else />
|
||||
</IconButton>
|
||||
<ButtonLink
|
||||
type="quiet"
|
||||
v-tooltip="formatMessage(commonMessages.openInBrowserButton)"
|
||||
:href="`https://modrinth.com/${project.project_type}/${project.slug}/version/${version.id}`"
|
||||
target="_blank"
|
||||
:aria-label="formatMessage(commonMessages.openInBrowserButton)"
|
||||
class="!w-9 !px-0 !rounded-full"
|
||||
>
|
||||
<ExternalIcon />
|
||||
</ButtonLink>
|
||||
</template>
|
||||
</ProjectPageVersions>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { CheckIcon, DownloadIcon, ExternalIcon, MoreVerticalIcon } from '@modrinth/assets'
|
||||
import { CheckIcon, DownloadIcon, ExternalIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
ButtonLink,
|
||||
IconButton,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
OverflowMenu,
|
||||
ProjectPageVersions,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import type { ContentInstallInstance, ContentInstallProjectInfo, ContentItem } from '@modrinth/ui'
|
||||
import { createContext, defineMessage, useVIntl } from '@modrinth/ui'
|
||||
import {
|
||||
createContext,
|
||||
defineMessage,
|
||||
getLatestMatchingInstallVersion,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||
import dayjs from 'dayjs'
|
||||
@@ -813,7 +818,22 @@ export function createContentInstall(opts: {
|
||||
}
|
||||
|
||||
if (project.project_type === 'modpack') {
|
||||
const version = versionId ?? project.versions[project.versions.length - 1]
|
||||
let version = versionId ?? null
|
||||
if (!version) {
|
||||
const hasHints = !!(hints?.preferredGameVersion || hints?.preferredLoader)
|
||||
if (hasHints) {
|
||||
const versions = (await get_version_many(
|
||||
project.versions,
|
||||
'must_revalidate',
|
||||
)) as Labrinth.Versions.v2.Version[]
|
||||
const matching = getLatestMatchingInstallVersion(versions, {
|
||||
gameVersions: hints?.preferredGameVersion ? [hints.preferredGameVersion] : undefined,
|
||||
loaders: hints?.preferredLoader ? [hints.preferredLoader] : undefined,
|
||||
})
|
||||
version = matching?.id ?? null
|
||||
}
|
||||
version ??= project.versions[project.versions.length - 1]
|
||||
}
|
||||
const packs = await list()
|
||||
const existingPack = packs.find((pack) => pack.link?.project_id === project.id)
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export const DEFAULT_FEATURE_FLAGS = {
|
||||
show_instance_play_time: true,
|
||||
advanced_filters_collapsed: true,
|
||||
always_show_copy_details: false,
|
||||
hide_installed_modpacks: false,
|
||||
}
|
||||
|
||||
export const THEME_OPTIONS = ['dark', 'light', 'oled', 'retro', 'system'] as const
|
||||
|
||||
+7
@@ -27,6 +27,7 @@ import {
|
||||
LineController,
|
||||
LineElement,
|
||||
PointElement,
|
||||
type ScriptableLineSegmentContext,
|
||||
Tooltip,
|
||||
} from 'chart.js'
|
||||
|
||||
@@ -446,6 +447,12 @@ function buildDatasets() {
|
||||
pointHoverBackgroundColor: colors.borderColor,
|
||||
pointHoverBorderWidth: 0,
|
||||
pointHitRadius: 16,
|
||||
segment: dataset.lastDataPointUnavailable
|
||||
? {
|
||||
borderColor: (context: ScriptableLineSegmentContext) =>
|
||||
context.p1DataIndex === dataset.data.length - 1 ? 'transparent' : undefined,
|
||||
}
|
||||
: undefined,
|
||||
stack: props.stacked ? 'analytics' : undefined,
|
||||
}
|
||||
})
|
||||
|
||||
+7
-11
@@ -12,23 +12,19 @@
|
||||
|
||||
<template #actions>
|
||||
<div class="flex justify-end gap-2">
|
||||
<ButtonStyled type="transparent">
|
||||
<button @click="modal?.hide()">
|
||||
{{ formatMessage(analyticsChartMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="orange">
|
||||
<button class="!shadow-none" @click="confirm">
|
||||
{{ formatMessage(analyticsChartMessages.showAll) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" @click="modal?.hide()">
|
||||
{{ formatMessage(analyticsChartMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="orange" @click="confirm">
|
||||
{{ formatMessage(analyticsChartMessages.showAll) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ButtonStyled, NewModal, useVIntl } from '@modrinth/ui'
|
||||
import { NewModal, useVIntl, Button } from '@modrinth/ui'
|
||||
|
||||
import { analyticsChartMessages } from '../../analytics-messages'
|
||||
|
||||
|
||||
+10
-2
@@ -59,7 +59,9 @@
|
||||
<span class="font-medium text-primary">
|
||||
{{ formatMessage(analyticsChartMessages.total) }}
|
||||
</span>
|
||||
<span class="font-semibold text-contrast">{{ formattedTotal }}</span>
|
||||
<span class="font-semibold" :class="totalHasData ? 'text-contrast' : 'text-primary'">
|
||||
{{ formattedTotal }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="entry in entries"
|
||||
@@ -111,7 +113,11 @@
|
||||
:class="[
|
||||
'shrink-0',
|
||||
entry.isPreviousPeriod ? 'font-medium text-secondary' : 'font-semibold',
|
||||
entry.hidden ? 'text-primary line-through opacity-70' : 'text-contrast',
|
||||
entry.hidden
|
||||
? 'text-primary line-through opacity-70'
|
||||
: entry.noData
|
||||
? 'text-primary'
|
||||
: 'text-contrast',
|
||||
]"
|
||||
>
|
||||
{{ entry.formattedValue }}
|
||||
@@ -148,6 +154,7 @@ export type AnalyticsChartTooltipEntry = {
|
||||
tooltip?: string
|
||||
color: string
|
||||
formattedValue: string
|
||||
noData: boolean
|
||||
hidden: boolean
|
||||
toggleDisabled: boolean
|
||||
isPreviousPeriod?: boolean
|
||||
@@ -164,6 +171,7 @@ const props = defineProps<{
|
||||
chartStart: Date | null
|
||||
chartEnd: Date | null
|
||||
formattedTotal: string
|
||||
totalHasData: boolean
|
||||
entries: AnalyticsChartTooltipEntry[]
|
||||
containerWidth: number
|
||||
containerHeight: number
|
||||
|
||||
+28
-3
@@ -64,6 +64,7 @@
|
||||
:chart-start="chartRangeBounds?.start ?? null"
|
||||
:chart-end="chartRangeBounds?.end ?? null"
|
||||
:formatted-total="hoverFormattedTotal"
|
||||
:total-has-data="hoverHasData"
|
||||
:entries="hoverEntries"
|
||||
:container-width="containerSize.width"
|
||||
:container-height="containerSize.height"
|
||||
@@ -89,6 +90,7 @@ import type {
|
||||
AnalyticsGroupByPreset,
|
||||
} from '~/providers/analytics/analytics'
|
||||
|
||||
import { analyticsChartMessages } from '../../analytics-messages.ts'
|
||||
import type {
|
||||
AnalyticsChartLegendEntry,
|
||||
AnalyticsChartRangeBounds,
|
||||
@@ -188,7 +190,26 @@ const hoverTotalValue = computed(() => {
|
||||
}, 0)
|
||||
})
|
||||
|
||||
function hasDatasetDataAtSlice(dataset: ChartDataset | undefined, sliceIndex: number) {
|
||||
if (!dataset) return false
|
||||
return !dataset.lastDataPointUnavailable || sliceIndex !== dataset.data.length - 1
|
||||
}
|
||||
|
||||
const hoverHasData = computed(() => {
|
||||
if (hoverState.sliceIndex === null) return false
|
||||
const sliceIndex = hoverState.sliceIndex
|
||||
|
||||
return props.currentLegendEntries.some((legendEntry) => {
|
||||
if (legendEntry.hidden) return false
|
||||
const dataset = props.chartDatasetById.get(legendEntry.id)
|
||||
return hasDatasetDataAtSlice(dataset, sliceIndex)
|
||||
})
|
||||
})
|
||||
|
||||
const hoverFormattedTotal = computed(() => {
|
||||
if (!hoverHasData.value) {
|
||||
return formatMessage(analyticsChartMessages.noData)
|
||||
}
|
||||
if (props.isRatioMode) {
|
||||
return hoverTotalValue.value > 0 ? '100%' : '0%'
|
||||
}
|
||||
@@ -203,6 +224,7 @@ const hoverEntries = computed(() => {
|
||||
return props.legendEntries.map((legendEntry) => {
|
||||
const dataset = props.chartDatasetById.get(legendEntry.id)
|
||||
const value = dataset?.data[sliceIndex] ?? 0
|
||||
const hasData = hasDatasetDataAtSlice(dataset, sliceIndex)
|
||||
const ratioValue = legendEntry.hidden || totalValue === 0 ? 0 : (value / totalValue) * 100
|
||||
return {
|
||||
projectId: legendEntry.id,
|
||||
@@ -210,9 +232,12 @@ const hoverEntries = computed(() => {
|
||||
projectName: legendEntry.projectName,
|
||||
tooltip: legendEntry.tooltip,
|
||||
color: legendEntry.color,
|
||||
formattedValue: props.isRatioMode
|
||||
? `${ratioValue.toFixed(1)}%`
|
||||
: formatMetricValue(value, props.activeStat, formatNumber, formatMessage),
|
||||
formattedValue: hasData
|
||||
? props.isRatioMode
|
||||
? `${ratioValue.toFixed(1)}%`
|
||||
: formatMetricValue(value, props.activeStat, formatNumber, formatMessage)
|
||||
: formatMessage(analyticsChartMessages.noData),
|
||||
noData: !hasData,
|
||||
hidden: legendEntry.hidden,
|
||||
toggleDisabled: !legendEntry.hidden && isLegendEntryToggleDisabled(legendEntry),
|
||||
isPreviousPeriod: legendEntry.isPreviousPeriod,
|
||||
|
||||
+1
@@ -38,6 +38,7 @@ export type ChartDataset = {
|
||||
projectName?: string
|
||||
tooltip?: string
|
||||
data: number[]
|
||||
lastDataPointUnavailable?: boolean
|
||||
borderColor: string
|
||||
backgroundColor: string
|
||||
borderDash?: number[]
|
||||
|
||||
+27
-4
@@ -32,6 +32,7 @@ import {
|
||||
type ChartDataset,
|
||||
getChartDatasetTotal,
|
||||
getShortHourlyAxisTickLimit,
|
||||
getSliceBucketRange,
|
||||
getSliceCount,
|
||||
shouldCapitalizeBreakdownLabel,
|
||||
} from './analytics-chart-utils'
|
||||
@@ -161,8 +162,8 @@ export function useAnalyticsChartDatasets(
|
||||
)
|
||||
: undefined
|
||||
})
|
||||
const chartDatasetsByStat = computed<Record<AnalyticsDashboardStat, ChartDataset[]>>(() =>
|
||||
buildDatasetsByStat(
|
||||
const chartDatasetsByStat = computed<Record<AnalyticsDashboardStat, ChartDataset[]>>(() => {
|
||||
const datasets = buildDatasetsByStat(
|
||||
context.displayedTimeSlices.value,
|
||||
selectedProjects.value,
|
||||
legendPalette.value,
|
||||
@@ -175,8 +176,20 @@ export function useAnalyticsChartDatasets(
|
||||
showProjectVersionNames.value ? context.getVersionProjectName : undefined,
|
||||
formatMessage,
|
||||
sliceCount.value,
|
||||
),
|
||||
)
|
||||
)
|
||||
const nextFetchRequest = context.displayedFetchRequest.value
|
||||
if (
|
||||
nextFetchRequest &&
|
||||
context.displayedSelectedGroupBy.value === 'day' &&
|
||||
isLastBucketCurrentDay(nextFetchRequest.time_range, sliceCount.value)
|
||||
) {
|
||||
datasets.revenue = datasets.revenue.map((dataset) => ({
|
||||
...dataset,
|
||||
lastDataPointUnavailable: true,
|
||||
}))
|
||||
}
|
||||
return datasets
|
||||
})
|
||||
const previousChartDatasetsByStat = computed<Record<AnalyticsDashboardStat, ChartDataset[]>>(() =>
|
||||
buildDatasetsByStat(
|
||||
context.displayedPreviousTimeSlices.value,
|
||||
@@ -406,6 +419,16 @@ function buildDatasetsByStat(
|
||||
return datasetsByStat
|
||||
}
|
||||
|
||||
function isLastBucketCurrentDay(timeRange: Labrinth.Analytics.v3.TimeRange, sliceCount: number) {
|
||||
const lastBucket = getSliceBucketRange(timeRange, sliceCount, sliceCount - 1)
|
||||
const todayStart = new Date()
|
||||
todayStart.setHours(0, 0, 0, 0)
|
||||
const tomorrowStart = new Date(todayStart)
|
||||
tomorrowStart.setDate(tomorrowStart.getDate() + 1)
|
||||
|
||||
return lastBucket.start < tomorrowStart && lastBucket.end > todayStart
|
||||
}
|
||||
|
||||
function sortDatasetsByTotal(datasets: ChartDataset[]) {
|
||||
return [...datasets]
|
||||
.sort((a, b) => {
|
||||
|
||||
@@ -688,6 +688,10 @@ export const analyticsChartMessages = defineMessages({
|
||||
id: 'analytics.chart.tooltip.hide-entry',
|
||||
defaultMessage: 'Hide {name} in graph',
|
||||
},
|
||||
noData: {
|
||||
id: 'analytics.chart.tooltip.no-data',
|
||||
defaultMessage: 'No data',
|
||||
},
|
||||
durationDays: {
|
||||
id: 'analytics.chart.tooltip.duration.days',
|
||||
defaultMessage: '{count, plural, one {# day} other {# days}}',
|
||||
|
||||
@@ -31,23 +31,21 @@
|
||||
wrapper-class="w-full sm:w-64"
|
||||
@focusin="selectSearchInputText"
|
||||
/>
|
||||
<ButtonStyled>
|
||||
<OverflowMenu
|
||||
class="!shadow-none"
|
||||
<TeleportOverflowMenu label="More options"
|
||||
class="!w-auto !px-2.5 !rounded-xl"
|
||||
:options="csvExportOptions"
|
||||
:disabled="isDataLoading || filteredRows.length === 0"
|
||||
>
|
||||
<DownloadIcon />
|
||||
{{ formatMessage(analyticsTableMessages.exportCsvButton) }}
|
||||
<DropdownIcon />
|
||||
<template #cumulative-csv>
|
||||
{{ formatMessage(analyticsTableMessages.cumulativeCsv) }}
|
||||
</template>
|
||||
<template #grouped-csv>
|
||||
{{ formatMessage(analyticsTableMessages.groupedCsv, { groupBy: groupByLabel }) }}
|
||||
</template>
|
||||
</OverflowMenu>
|
||||
</ButtonStyled>
|
||||
<DownloadIcon />
|
||||
{{ formatMessage(analyticsTableMessages.exportCsvButton) }}
|
||||
<DropdownIcon />
|
||||
<template #cumulative-csv>
|
||||
{{ formatMessage(analyticsTableMessages.cumulativeCsv) }}
|
||||
</template>
|
||||
<template #grouped-csv>
|
||||
{{ formatMessage(analyticsTableMessages.groupedCsv, { groupBy: groupByLabel }) }}
|
||||
</template>
|
||||
</TeleportOverflowMenu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -190,10 +188,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { TeleportOverflowMenu } from '@modrinth/ui'
|
||||
import { DownloadIcon, DropdownIcon, SearchIcon, UserIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
OverflowMenu,
|
||||
type OverflowMenuOption,
|
||||
Pagination,
|
||||
StyledInput,
|
||||
@@ -370,10 +367,12 @@ const csvExportOptions = computed<OverflowMenuOption[]>(() => {
|
||||
return [
|
||||
{
|
||||
id: 'cumulative-csv',
|
||||
label: formatMessage(analyticsTableMessages.cumulativeCsv),
|
||||
action: () => downloadCsv('breakdown_only'),
|
||||
},
|
||||
{
|
||||
id: 'grouped-csv',
|
||||
label: formatMessage(analyticsTableMessages.groupedCsv, { groupBy: groupByLabel.value }),
|
||||
action: () => downloadCsv('date_breakdown'),
|
||||
},
|
||||
]
|
||||
@@ -384,6 +383,10 @@ const csvExportOptions = computed<OverflowMenuOption[]>(() => {
|
||||
return [
|
||||
{
|
||||
id: mode === 'date_breakdown' ? 'grouped-csv' : 'cumulative-csv',
|
||||
label:
|
||||
mode === 'date_breakdown'
|
||||
? formatMessage(analyticsTableMessages.groupedCsv, { groupBy: groupByLabel.value })
|
||||
: formatMessage(analyticsTableMessages.cumulativeCsv),
|
||||
action: () => downloadCsv(mode),
|
||||
},
|
||||
]
|
||||
|
||||
@@ -6,25 +6,21 @@
|
||||
{{ formatMessage(analyticsMessages.title) }}
|
||||
</span>
|
||||
<div class="flex flex-wrap items-center justify-end gap-2">
|
||||
<ButtonStyled type="transparent">
|
||||
<button
|
||||
type="button"
|
||||
<Button type="quiet"
|
||||
native-type="button"
|
||||
:disabled="isAnalyticsQueryBuilderDefault"
|
||||
@click="resetAnalyticsQueryBuilder"
|
||||
>
|
||||
{{ formatMessage(analyticsMessages.resetButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled type="outlined">
|
||||
<button
|
||||
type="button"
|
||||
{{ formatMessage(analyticsMessages.resetButton) }}
|
||||
</Button>
|
||||
<Button type="outlined"
|
||||
native-type="button"
|
||||
:disabled="projects.length === 0 || !fetchRequest || isRefetching"
|
||||
@click="refreshAnalyticsQuery"
|
||||
>
|
||||
<RefreshCwIcon :class="isRefetching ? 'animate-spin' : ''" />
|
||||
{{ formatMessage(analyticsMessages.refreshButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<RefreshCwIcon :class="isRefetching ? 'animate-spin' : ''" />
|
||||
{{ formatMessage(analyticsMessages.refreshButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<QueryBuilder />
|
||||
@@ -36,8 +32,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { RefreshCwIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, injectProjectPageContext, useVIntl } from '@modrinth/ui'
|
||||
import { injectProjectPageContext, useVIntl } from '@modrinth/ui'
|
||||
|
||||
import {
|
||||
createAnalyticsDashboardContext,
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
:min-date="ANALYTICS_START_DATE_INPUT_VALUE"
|
||||
:now-timestamp="queryRefreshTimestamp"
|
||||
:trigger-class="triggerClass"
|
||||
:trigger-type="triggerType"
|
||||
:trigger-size="triggerSize"
|
||||
:trigger-interaction="triggerInteraction"
|
||||
@open="handleTimeframeOpen"
|
||||
@commit="handleTimeframeCommit"
|
||||
@apply="handleTimeframeApply"
|
||||
@@ -23,6 +26,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
type ButtonInteraction,
|
||||
type ButtonSize,
|
||||
type ButtonType,
|
||||
type ComboboxOption,
|
||||
TimeFramePicker as BaseTimeFramePicker,
|
||||
type TimeFramePickerSelection,
|
||||
@@ -58,6 +64,9 @@ const {
|
||||
|
||||
defineProps<{
|
||||
triggerClass?: string
|
||||
triggerType?: ButtonType
|
||||
triggerSize?: ButtonSize
|
||||
triggerInteraction?: ButtonInteraction
|
||||
}>()
|
||||
|
||||
const draftSelectedGroupBy = ref(selectedGroupBy.value)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
:max-tag-rows="1"
|
||||
:trigger-class="analyticsQueryChipTriggerClass"
|
||||
fit-content
|
||||
trigger-type="base"
|
||||
checkbox-position="right"
|
||||
show-selection-actions
|
||||
@open="handleProjectSelectOpen"
|
||||
@@ -32,7 +33,7 @@
|
||||
decoding="async"
|
||||
/>
|
||||
<LayersIcon v-else class="size-5 shrink-0 text-primary" />
|
||||
<span class="min-w-0 flex-1 truncate px-0.5 font-semibold text-primary">
|
||||
<span class="min-w-0 flex-1 truncate px-0.5 font-semibold text-inherit">
|
||||
{{ selectedProjectLabel }}
|
||||
</span>
|
||||
<ChevronLeftIcon
|
||||
@@ -145,6 +146,7 @@
|
||||
:dropdown-min-width="QUERY_BUILDER_DROPDOWN_MIN_WIDTH"
|
||||
:display-value="selectedGroupByLabel"
|
||||
:trigger-class="analyticsQueryChipTriggerClass"
|
||||
trigger-type="base"
|
||||
>
|
||||
<template #prefix>
|
||||
<ClockIcon class="size-5 shrink-0 text-primary" />
|
||||
@@ -160,6 +162,7 @@
|
||||
:dropdown-min-width="QUERY_BUILDER_DROPDOWN_MIN_WIDTH"
|
||||
:trigger-class="analyticsQueryChipTriggerClass"
|
||||
fit-content
|
||||
trigger-type="base"
|
||||
checkbox-position="right"
|
||||
:placeholder="formatMessage(analyticsMessages.none)"
|
||||
show-selection-actions
|
||||
@@ -170,7 +173,7 @@
|
||||
<div class="flex min-h-7 min-w-0 max-w-full flex-1 items-center gap-1.5 pr-1">
|
||||
<BlocksIcon class="size-5 shrink-0 text-primary" />
|
||||
<span
|
||||
class="min-w-0 flex-1 truncate px-0.5 font-semibold text-primary"
|
||||
class="min-w-0 flex-1 truncate px-0.5 font-semibold text-inherit"
|
||||
:title="mobileSelectedBreakdownLabel"
|
||||
>
|
||||
{{ mobileSelectedBreakdownLabel }}
|
||||
@@ -214,6 +217,7 @@
|
||||
:searchable="projectOptions.length > 6"
|
||||
fuzzy-search
|
||||
:max-tag-rows="1"
|
||||
trigger-type="base"
|
||||
checkbox-position="right"
|
||||
show-selection-actions
|
||||
@open="handleProjectSelectOpen"
|
||||
@@ -243,7 +247,7 @@
|
||||
class="size-5 shrink-0 text-primary"
|
||||
/>
|
||||
<BoxIcon v-else class="size-5 shrink-0 text-primary" />
|
||||
<span class="min-w-0 flex-1 truncate px-1.5 font-semibold text-primary">
|
||||
<span class="min-w-0 flex-1 truncate px-1.5 font-semibold text-inherit">
|
||||
{{ selectedProjectLabel }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -372,18 +376,17 @@
|
||||
:options="groupByOptions"
|
||||
:max-height="QUERY_BUILDER_DROPDOWN_MAX_HEIGHT"
|
||||
:dropdown-min-width="QUERY_BUILDER_DROPDOWN_MIN_WIDTH"
|
||||
trigger-type="base"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ButtonStyled v-if="!isTimeframeAndGroupByDefault" type="transparent">
|
||||
<button
|
||||
type="button"
|
||||
<Button type="quiet" v-if="!isTimeframeAndGroupByDefault"
|
||||
native-type="button"
|
||||
:disabled="isTimeframeAndGroupByDefault"
|
||||
@click="resetTimeframeAndGroupBy"
|
||||
>
|
||||
{{ formatMessage(analyticsMessages.resetButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
{{ formatMessage(analyticsMessages.resetButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-start gap-2">
|
||||
@@ -403,6 +406,7 @@
|
||||
:max-height="QUERY_BUILDER_DROPDOWN_MAX_HEIGHT"
|
||||
:dropdown-width="QUERY_BUILDER_DROPDOWN_MIN_WIDTH"
|
||||
:dropdown-min-width="QUERY_BUILDER_DROPDOWN_MIN_WIDTH"
|
||||
trigger-type="base"
|
||||
checkbox-position="right"
|
||||
:placeholder="formatMessage(analyticsMessages.none)"
|
||||
show-selection-actions
|
||||
@@ -412,7 +416,7 @@
|
||||
<template #input-content="{ isOpen, openDirection }">
|
||||
<div class="flex min-h-7 min-w-0 flex-1 items-center gap-1.5 pr-1">
|
||||
<span
|
||||
class="min-w-0 flex-1 truncate font-semibold text-primary"
|
||||
class="min-w-0 flex-1 truncate font-semibold text-inherit"
|
||||
:title="selectedBreakdownLabel"
|
||||
>
|
||||
{{ selectedBreakdownLabel }}
|
||||
@@ -441,6 +445,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import {
|
||||
BlocksIcon,
|
||||
@@ -454,7 +459,6 @@ import {
|
||||
UserIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Combobox,
|
||||
type ComboboxOption,
|
||||
MultiSelect,
|
||||
|
||||
@@ -12,12 +12,10 @@
|
||||
{{ formatMessage(analyticsStatCardMessages.monetizationBannerBody) }}
|
||||
</div>
|
||||
<template #actions>
|
||||
<ButtonStyled color="blue">
|
||||
<a href="https://modrinth.com/legal/cmp-info" target="_blank" class="w-fit !px-4">
|
||||
{{ formatMessage(analyticsStatCardMessages.monetizationBannerLearnMore) }}
|
||||
<RightArrowIcon aria-hidden="true" />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonLink type="colored" color="blue" href="https://modrinth.com/legal/cmp-info" target="_blank" class="w-fit !px-4">
|
||||
{{ formatMessage(analyticsStatCardMessages.monetizationBannerLearnMore) }}
|
||||
<RightArrowIcon aria-hidden="true" />
|
||||
</ButtonLink>
|
||||
</template>
|
||||
</Admonition>
|
||||
<div class="grid grid-cols-2 gap-3 lg:grid-cols-4">
|
||||
@@ -38,8 +36,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ButtonLink } from '@modrinth/ui'
|
||||
import { RightArrowIcon } from '@modrinth/assets'
|
||||
import { Admonition, ButtonStyled, useFormatNumber, useVIntl } from '@modrinth/ui'
|
||||
import { Admonition, useFormatNumber, useVIntl } from '@modrinth/ui'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
|
||||
import {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<ButtonStyled v-if="!!slots.title" :type="type">
|
||||
<button class="!w-full" @click="() => (isOpen ? close() : open())">
|
||||
<slot name="title" /><DropdownIcon
|
||||
class="ml-auto size-5 text-contrast transition-transform duration-300"
|
||||
:class="{ 'rotate-180': isOpen }"
|
||||
/>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button :type="(type) === 'transparent' ? 'quiet' : (type) === 'standard' ? 'base' : (type) === 'highlight' || (type) === 'highlight-colored-text' || (type) === 'chip' ? 'base' : (type)" v-if="!!slots.title" class="!w-full" @click="() => (isOpen ? close() : open())">
|
||||
<slot name="title" /><DropdownIcon
|
||||
class="ml-auto size-5 text-contrast transition-transform duration-300"
|
||||
:class="{ 'rotate-180': isOpen }"
|
||||
/>
|
||||
</Button>
|
||||
<div class="accordion-content" :class="{ open: isOpen }">
|
||||
<div>
|
||||
<div :class="{ 'mt-2': !!slots.title }" v-bind="$attrs" :inert="!isOpen">
|
||||
@@ -19,8 +17,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { DropdownIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled } from '@modrinth/ui'
|
||||
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
|
||||
@@ -40,12 +40,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="emit('revoke', authorization.app_id)">
|
||||
<XCircleIcon />
|
||||
{{ formatMessage(messages.revokeAction) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="red" @click="emit('revoke', authorization.app_id)">
|
||||
<XCircleIcon />
|
||||
{{ formatMessage(messages.revokeAction) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showUnofficialDisclosure" class="flex items-center gap-1 text-orange">
|
||||
@@ -78,11 +76,11 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { BadgeCheckIcon, CheckIcon, IssuesIcon, XCircleIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
IntlFormatted,
|
||||
|
||||
@@ -57,26 +57,23 @@
|
||||
</div>
|
||||
<template #actions>
|
||||
<div class="flex justify-end gap-2">
|
||||
<ButtonStyled>
|
||||
<button @click="hide">
|
||||
<XIcon />
|
||||
Cancel
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button :disabled="!isConfirmEnabled" @click="onConfirmClick">
|
||||
<TransferIcon />
|
||||
Transfer ownership
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="hide">
|
||||
<XIcon />
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="colored" color="red" :disabled="!isConfirmEnabled" @click="onConfirmClick">
|
||||
<TransferIcon />
|
||||
Transfer ownership
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { RightArrowIcon, TransferIcon, XIcon } from '@modrinth/assets'
|
||||
import { Admonition, Avatar, ButtonStyled, NewModal, StyledInput } from '@modrinth/ui'
|
||||
import { Admonition, Avatar, NewModal, StyledInput } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -64,26 +64,23 @@
|
||||
</div>
|
||||
<template #actions>
|
||||
<div class="flex justify-end gap-2">
|
||||
<ButtonStyled>
|
||||
<button @click="hide">
|
||||
<XIcon />
|
||||
Cancel
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button :disabled="!isConfirmEnabled" @click="onConfirmClick">
|
||||
<TransferIcon />
|
||||
Transfer ownership
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="hide">
|
||||
<XIcon />
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="colored" color="red" :disabled="!isConfirmEnabled" @click="onConfirmClick">
|
||||
<TransferIcon />
|
||||
Transfer ownership
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { RightArrowIcon, TransferIcon, XIcon } from '@modrinth/assets'
|
||||
import { Admonition, Avatar, ButtonStyled, NewModal, StyledInput } from '@modrinth/ui'
|
||||
import { Admonition, Avatar, NewModal, StyledInput } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ButtonLink } from '@modrinth/ui'
|
||||
import {
|
||||
BlueskyIcon,
|
||||
DiscordIcon,
|
||||
@@ -9,7 +10,6 @@ import {
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
AutoLink,
|
||||
ButtonStyled,
|
||||
defineMessage,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
@@ -271,31 +271,23 @@ function developerModeIncrement() {
|
||||
class="text-logo button-base h-6 w-auto text-contrast lg:h-8"
|
||||
@click="developerModeIncrement()"
|
||||
/>
|
||||
<ButtonStyled v-if="flags.developerMode" circular type="transparent" color="brand">
|
||||
<nuxt-link
|
||||
<ButtonLink type="quiet" color="brand" v-if="flags.developerMode"
|
||||
v-tooltip="formatMessage(commonSettingsMessages.featureFlags)"
|
||||
to="/settings/flags"
|
||||
>
|
||||
<ToggleRightIcon />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
class="!w-9 !px-0 !rounded-full">
|
||||
<ToggleRightIcon />
|
||||
</ButtonLink>
|
||||
</div>
|
||||
<div class="flex flex-wrap justify-center gap-px sm:-mx-2">
|
||||
<ButtonStyled
|
||||
v-for="(social, index) in socialLinks"
|
||||
<ButtonLink type="quiet" v-for="(social, index) in socialLinks"
|
||||
:key="`footer-social-${index}`"
|
||||
circular
|
||||
type="transparent"
|
||||
>
|
||||
<a
|
||||
v-tooltip="formatMessage(social.label)"
|
||||
:href="social.href"
|
||||
target="_blank"
|
||||
:rel="`noopener${social.rel ? ` ${social.rel}` : ''}`"
|
||||
>
|
||||
<component :is="social.icon" class="h-5 w-5" />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
class="!w-9 !px-0 !rounded-full">
|
||||
<component :is="social.icon" class="h-5 w-5" />
|
||||
</ButtonLink>
|
||||
</div>
|
||||
<div class="mt-auto flex flex-wrap justify-center gap-3 md:flex-col">
|
||||
<p class="m-0">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { CheckIcon, MailIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, defineMessages, injectModrinthClient, useVIntl } from '@modrinth/ui'
|
||||
import { defineMessages, injectModrinthClient, useVIntl } from '@modrinth/ui'
|
||||
import { useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
@@ -61,12 +62,10 @@ async function subscribe() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ButtonStyled v-if="isSuccess && showSubscribeButton" color="brand" type="outlined">
|
||||
<button v-tooltip="formatMessage(messages.tooltipSubscribe)" @click="subscribe">
|
||||
<template v-if="!showSubscriptionConfirmation">
|
||||
<MailIcon /> {{ formatMessage(messages.subscribe) }}
|
||||
</template>
|
||||
<template v-else> <CheckIcon /> {{ formatMessage(messages.subscribed) }} </template>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" v-if="isSuccess && showSubscribeButton" v-tooltip="formatMessage(messages.tooltipSubscribe)" @click="subscribe" class="!text-brand [&>svg]:!text-brand !shadow-[inset_0_0_0_1px_var(--color-brand)]">
|
||||
<template v-if="!showSubscriptionConfirmation">
|
||||
<MailIcon /> {{ formatMessage(messages.subscribe) }}
|
||||
</template>
|
||||
<template v-else> <CheckIcon /> {{ formatMessage(messages.subscribed) }} </template>
|
||||
</Button>
|
||||
</template>
|
||||
|
||||
@@ -46,18 +46,14 @@
|
||||
class="flex flex-wrap items-center gap-3"
|
||||
:class="{ 'gap-2': compact }"
|
||||
>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="performActionByTitle(notification, 'Accept')">
|
||||
<CheckIcon />
|
||||
Accept
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="performActionByTitle(notification, 'Deny')">
|
||||
<XIcon />
|
||||
Decline
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="performActionByTitle(notification, 'Accept')">
|
||||
<CheckIcon />
|
||||
Accept
|
||||
</Button>
|
||||
<Button type="colored" color="red" @click="performActionByTitle(notification, 'Deny')">
|
||||
<XIcon />
|
||||
Decline
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
class="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-[var(--color-text-secondary)]"
|
||||
@@ -276,8 +272,7 @@
|
||||
</span>
|
||||
<div v-if="compact" class="notification__actions">
|
||||
<template v-if="type === 'team_invite' || type === 'organization_invite'">
|
||||
<ButtonStyled circular color="brand" type="transparent">
|
||||
<button
|
||||
<IconButton type="quiet" color="brand" :label="`Accept`"
|
||||
v-tooltip="`Accept`"
|
||||
@click="
|
||||
() => {
|
||||
@@ -286,11 +281,9 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<CheckIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular color="red" type="transparent">
|
||||
<button
|
||||
<CheckIcon />
|
||||
</IconButton>
|
||||
<IconButton type="quiet" color="red" :label="`Decline`"
|
||||
v-tooltip="`Decline`"
|
||||
@click="
|
||||
() => {
|
||||
@@ -299,23 +292,19 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<XIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
<ButtonStyled v-else-if="!notification.read" circular type="transparent">
|
||||
<button v-tooltip="`Mark as read`" @click="read()">
|
||||
<XIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</IconButton>
|
||||
</template>
|
||||
<IconButton type="quiet" :label="`Mark as read`" v-else-if="!notification.read" v-tooltip="`Mark as read`" @click="read()">
|
||||
<XIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div v-else class="notification__actions">
|
||||
<div v-if="type !== null" class="input-group">
|
||||
<template
|
||||
v-if="(type === 'team_invite' || type === 'organization_invite') && !notification.read"
|
||||
>
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
<Button type="colored" color="brand"
|
||||
@click="
|
||||
() => {
|
||||
acceptTeamInvite(notification.body.team_id)
|
||||
@@ -323,12 +312,10 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<CheckIcon />
|
||||
Accept
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button
|
||||
<CheckIcon />
|
||||
Accept
|
||||
</Button>
|
||||
<Button type="colored" color="red"
|
||||
@click="
|
||||
() => {
|
||||
removeSelfFromTeam(notification.body.team_id)
|
||||
@@ -336,39 +323,30 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<XIcon />
|
||||
Decline
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<XIcon />
|
||||
Decline
|
||||
</Button>
|
||||
</template>
|
||||
<ButtonStyled v-else-if="!notification.read">
|
||||
<button @click="read()">
|
||||
<CheckIcon />
|
||||
Mark as read
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button v-else-if="!notification.read" @click="read()">
|
||||
<CheckIcon />
|
||||
Mark as read
|
||||
</Button>
|
||||
<CopyCode v-if="flags.developerMode" :text="notification.id" />
|
||||
</div>
|
||||
<div v-else class="input-group">
|
||||
<ButtonStyled v-if="notification.link && notification.link !== '#'">
|
||||
<nuxt-link :to="notification.link" target="_blank">
|
||||
<ExternalIcon />
|
||||
Open link
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-for="(action, actionIndex) in notification.actions" :key="actionIndex">
|
||||
<button @click="performAction(notification, actionIndex)">
|
||||
<CheckIcon v-if="action.title === 'Accept'" />
|
||||
<XIcon v-else-if="action.title === 'Deny'" />
|
||||
{{ action.title }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-if="notification.actions.length === 0 && !notification.read">
|
||||
<button @click="performAction(notification, null)">
|
||||
<CheckIcon />
|
||||
Mark as read
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonLink v-if="notification.link && notification.link !== '#'" :to="notification.link" target="_blank">
|
||||
<ExternalIcon />
|
||||
Open link
|
||||
</ButtonLink>
|
||||
<Button v-for="(action, actionIndex) in notification.actions" :key="actionIndex" @click="performAction(notification, actionIndex)">
|
||||
<CheckIcon v-if="action.title === 'Accept'" />
|
||||
<XIcon v-else-if="action.title === 'Deny'" />
|
||||
{{ action.title }}
|
||||
</Button>
|
||||
<Button v-if="notification.actions.length === 0 && !notification.read" @click="performAction(notification, null)">
|
||||
<CheckIcon />
|
||||
Mark as read
|
||||
</Button>
|
||||
<CopyCode v-if="flags.developerMode" :text="notification.id" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -377,6 +355,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Button, ButtonLink, IconButton } from '@modrinth/ui'
|
||||
import {
|
||||
BellIcon,
|
||||
CalendarIcon,
|
||||
@@ -390,7 +369,6 @@ import {
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
Categories,
|
||||
CopyCode,
|
||||
DoubleIcon,
|
||||
|
||||
@@ -38,27 +38,22 @@
|
||||
|
||||
<template #actions>
|
||||
<PageHeaderActions>
|
||||
<ButtonStyled v-if="canManage" size="large">
|
||||
<nuxt-link :to="`/organization/${organization.slug}/settings`">
|
||||
<SettingsIcon />
|
||||
{{ formatMessage(messages.manage) }}
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular size="large" type="transparent">
|
||||
<TeleportOverflowMenu
|
||||
<ButtonLink size="xl" v-if="canManage" :to="`/organization/${organization.slug}/settings`">
|
||||
<SettingsIcon />
|
||||
{{ formatMessage(messages.manage) }}
|
||||
</ButtonLink>
|
||||
<TeleportOverflowMenu type="quiet" size="xl" :label="formatMessage(commonMessages.moreOptionsButton)"
|
||||
:options="moreActions"
|
||||
:tooltip="formatMessage(commonMessages.moreOptionsButton)"
|
||||
:aria-label="formatMessage(commonMessages.moreOptionsButton)"
|
||||
>
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</ButtonStyled>
|
||||
<MoreVerticalIcon />
|
||||
</TeleportOverflowMenu>
|
||||
</PageHeaderActions>
|
||||
</template>
|
||||
</PageHeader>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ButtonLink, TeleportOverflowMenu } from '@modrinth/ui'
|
||||
import {
|
||||
BoxIcon,
|
||||
ClipboardCopyIcon,
|
||||
@@ -70,7 +65,6 @@ import {
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
PageHeader,
|
||||
@@ -78,8 +72,7 @@ import {
|
||||
PageHeaderBadgeItem,
|
||||
PageHeaderMetadata,
|
||||
PageHeaderMetadataNumberItem,
|
||||
TeleportOverflowMenu,
|
||||
type TeleportOverflowMenuItem,
|
||||
type OverflowMenuOption,
|
||||
useFormatNumber,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -135,7 +128,7 @@ const emit = defineEmits<{
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatNumber = useFormatNumber()
|
||||
|
||||
const moreActions = computed<TeleportOverflowMenuItem[]>(() => [
|
||||
const moreActions = computed<OverflowMenuOption[]>(() => [
|
||||
{
|
||||
id: 'manage-projects',
|
||||
label: formatMessage(messages.manageProjects),
|
||||
@@ -143,10 +136,8 @@ const moreActions = computed<TeleportOverflowMenuItem[]>(() => [
|
||||
action: () => emit('manageProjects'),
|
||||
shown: props.canManage,
|
||||
},
|
||||
{
|
||||
divider: true,
|
||||
shown: props.canManage,
|
||||
},
|
||||
{ type: 'divider', shown: props.canManage,
|
||||
},
|
||||
{
|
||||
id: 'copy-id',
|
||||
label: formatMessage(commonMessages.copyIdButton),
|
||||
|
||||
@@ -65,38 +65,32 @@
|
||||
</div>
|
||||
<template #actions>
|
||||
<div class="flex justify-end gap-2">
|
||||
<ButtonStyled type="outlined">
|
||||
<button @click="hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="selectedProjects.length === 0" @click="submitTransfer()">
|
||||
<TransferIcon />
|
||||
{{
|
||||
formatMessage(messages.transferSelectedProjects, {
|
||||
count: selectedProjects.length,
|
||||
})
|
||||
}}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="outlined" @click="hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" :disabled="selectedProjects.length === 0" @click="submitTransfer()">
|
||||
<TransferIcon />
|
||||
{{
|
||||
formatMessage(messages.transferSelectedProjects, {
|
||||
count: selectedProjects.length,
|
||||
})
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
<ButtonStyled>
|
||||
<button @click="show($event)">
|
||||
<TransferIcon />
|
||||
<span>{{ formatMessage(messages.transferProjectsTitle) }}</span>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="show($event)">
|
||||
<TransferIcon />
|
||||
<span>{{ formatMessage(messages.transferProjectsTitle) }}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { BoxIcon, TransferIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
commonMessages,
|
||||
CopyCode,
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
<template>
|
||||
<ButtonStyled size="large" circular>
|
||||
<PopoutMenu
|
||||
v-if="authUser"
|
||||
:tooltip="
|
||||
<TeleportPopoutMenu
|
||||
v-if="authUser"
|
||||
icon-only
|
||||
size="xl"
|
||||
:label="
|
||||
saved ? formatMessage(commonMessages.savedLabel) : formatMessage(commonMessages.saveButton)
|
||||
"
|
||||
from="top-right"
|
||||
:aria-label="formatMessage(commonMessages.saveButton)"
|
||||
:dropdown-id="`${baseId}-save`"
|
||||
>
|
||||
placement="top-end"
|
||||
>
|
||||
<template #trigger>
|
||||
<BookmarkIcon aria-hidden="true" :fill="saved ? 'currentColor' : 'none'" />
|
||||
<template #menu>
|
||||
</template>
|
||||
<template #panel>
|
||||
<StyledInput
|
||||
v-model="displayCollectionsSearch"
|
||||
:placeholder="formatMessage(commonMessages.searchPlaceholder)"
|
||||
@@ -31,33 +32,33 @@
|
||||
<div v-else class="menu-text">
|
||||
<p class="popout-text">{{ noCollectionsLabel }}</p>
|
||||
</div>
|
||||
<ButtonStyled>
|
||||
<button class="mx-3 mb-3" @click="createCollection">
|
||||
<Button class="mx-3 mb-3" @click="createCollection">
|
||||
<PlusIcon aria-hidden="true" />
|
||||
{{ createNewCollectionLabel }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</PopoutMenu>
|
||||
<nuxt-link
|
||||
v-else
|
||||
v-tooltip="formatMessage(commonMessages.saveButton)"
|
||||
:to="signInRoute"
|
||||
:aria-label="formatMessage(commonMessages.saveButton)"
|
||||
>
|
||||
<BookmarkIcon aria-hidden="true" />
|
||||
</nuxt-link>
|
||||
</ButtonStyled>
|
||||
</Button>
|
||||
</template>
|
||||
</TeleportPopoutMenu>
|
||||
<ButtonLink
|
||||
v-else
|
||||
size="xl"
|
||||
v-tooltip="formatMessage(commonMessages.saveButton)"
|
||||
:to="signInRoute"
|
||||
:aria-label="formatMessage(commonMessages.saveButton)"
|
||||
class="!w-12 !px-0 !rounded-full"
|
||||
>
|
||||
<BookmarkIcon aria-hidden="true" />
|
||||
</ButtonLink>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { BookmarkIcon, PlusIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Button,
|
||||
ButtonLink,
|
||||
Checkbox,
|
||||
commonMessages,
|
||||
PopoutMenu,
|
||||
StyledInput,
|
||||
TeleportPopoutMenu,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
@@ -48,13 +48,10 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<ButtonStyled
|
||||
<ButtonLink
|
||||
v-if="primaryFile && showDownload"
|
||||
:color="color"
|
||||
:type="type"
|
||||
:circular="circular"
|
||||
>
|
||||
<a
|
||||
:type="downloadButtonType"
|
||||
:color="downloadButtonColor"
|
||||
v-tooltip="circular ? formatMessage(messages.download) : null"
|
||||
:href="primaryFileDownloadUrl"
|
||||
:download="primaryFile.filename"
|
||||
@@ -64,21 +61,21 @@
|
||||
})
|
||||
"
|
||||
@click="emit('download')"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
<template v-if="!circular">
|
||||
{{ formatMessage(messages.download) }}
|
||||
</template>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
:class="circular ? '!w-9 !px-0 !rounded-full' : undefined"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
<template v-if="!circular">
|
||||
{{ formatMessage(messages.download) }}
|
||||
</template>
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ButtonLink } from '@modrinth/ui'
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { DownloadIcon, RadioButtonCheckedIcon, RadioButtonIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
type CdnDownloadReason,
|
||||
defineMessages,
|
||||
truncatedTooltip,
|
||||
@@ -161,6 +158,17 @@ const publishedLabel = computed(() =>
|
||||
capitalizeString(formatRelativeTime(props.version.date_published)),
|
||||
)
|
||||
const publishedTooltip = computed(() => formatDateTime(props.version.date_published))
|
||||
const downloadButtonType = computed(() => {
|
||||
if (props.type === 'transparent') return 'quiet'
|
||||
return props.color === 'brand' ? 'colored' : 'base'
|
||||
})
|
||||
const downloadButtonColor = computed(() =>
|
||||
downloadButtonType.value === 'colored' || downloadButtonType.value === 'quiet'
|
||||
? props.color === 'brand'
|
||||
? 'brand'
|
||||
: undefined
|
||||
: undefined,
|
||||
)
|
||||
const primaryFileSizeLabel = computed(() => {
|
||||
if (!primaryFile.value) return ''
|
||||
return formatBytes(primaryFile.value.size)
|
||||
|
||||
@@ -56,26 +56,21 @@
|
||||
{{ metadataLabel }}
|
||||
</span>
|
||||
</span>
|
||||
<ButtonStyled v-if="dependency.downloadHref" circular type="transparent">
|
||||
<a
|
||||
<ButtonLink type="quiet" v-if="dependency.downloadHref"
|
||||
v-tooltip="downloadTooltip"
|
||||
:href="dependency.downloadHref"
|
||||
:download="dependency.filename"
|
||||
:aria-label="downloadTooltip"
|
||||
@click="emit('download')"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" class="size-6 text-secondary" />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled v-else circular type="transparent">
|
||||
<button
|
||||
class="!w-9 !px-0 !rounded-full">
|
||||
<DownloadIcon aria-hidden="true" class="size-6 text-secondary" />
|
||||
</ButtonLink>
|
||||
<IconButton type="quiet" :label="dependency.unavailableTooltip" v-else
|
||||
v-tooltip="dependency.unavailableTooltip"
|
||||
disabled
|
||||
:aria-label="dependency.unavailableTooltip"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" class="size-6 text-secondary" />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<DownloadIcon aria-hidden="true" class="size-6 text-secondary" />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div
|
||||
v-for="childDependency in dependency.dependencies"
|
||||
@@ -97,10 +92,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { IconButton, ButtonLink } from '@modrinth/ui'
|
||||
import { DownloadIcon, PackageIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
TagItem,
|
||||
truncatedTooltip,
|
||||
|
||||
@@ -7,18 +7,16 @@
|
||||
class="modrinth-app-section contents"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<ButtonStyled color="brand">
|
||||
<a
|
||||
<ButtonLink type="colored" color="brand"
|
||||
class="!min-h-10 w-fit no-underline"
|
||||
:href="`modrinth://mod/${project.slug}`"
|
||||
@click="installWithApp"
|
||||
>
|
||||
<ModrinthIcon aria-hidden="true" />
|
||||
<span class="min-w-0 text-center">
|
||||
{{ formatMessage(messages.installWithModrinthApp) }}
|
||||
</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ModrinthIcon aria-hidden="true" />
|
||||
<span class="min-w-0 text-center">
|
||||
{{ formatMessage(messages.installWithModrinthApp) }}
|
||||
</span>
|
||||
</ButtonLink>
|
||||
<Accordion ref="getModrinthAppAccordion">
|
||||
<nuxt-link class="mt-2 flex justify-center text-brand-blue hover:underline" to="/app">
|
||||
{{ formatMessage(messages.dontHaveModrinthApp) }}
|
||||
@@ -37,9 +35,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ButtonLink } from '@modrinth/ui'
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { ModrinthIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import type { DisplayProjectType } from '@modrinth/utils'
|
||||
import { ref } from 'vue'
|
||||
|
||||
|
||||
@@ -49,59 +49,62 @@
|
||||
</template>
|
||||
<template v-if="showDependencyDownloadActions" #actions>
|
||||
<div class="flex flex-wrap justify-end gap-2 p-2">
|
||||
<ButtonStyled>
|
||||
<button
|
||||
class="!shadow-none"
|
||||
<Button
|
||||
:disabled="!!downloadingActionType || !dependencyDownloadFilesLoaded"
|
||||
@click="downloadSelectedVersionZip"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="downloadingActionType === 'zip'"
|
||||
aria-hidden="true"
|
||||
class="animate-spin"
|
||||
/>
|
||||
<DownloadIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.downloadAsZip) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<JoinedButtons
|
||||
<SpinnerIcon
|
||||
v-if="downloadingActionType === 'zip'"
|
||||
aria-hidden="true"
|
||||
class="animate-spin"
|
||||
/>
|
||||
<DownloadIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.downloadAsZip) }}
|
||||
</Button>
|
||||
<SplitButton
|
||||
v-if="hasRecommendedDownloadFiles"
|
||||
type="colored"
|
||||
color="brand"
|
||||
:actions="downloadWithRecommendedActions"
|
||||
:options="downloadWithRecommendedOptions"
|
||||
:menu-label="formatMessage(messages.downloadWithRecommended)"
|
||||
:disabled="!!downloadingActionType || !dependencyDownloadFilesLoaded"
|
||||
/>
|
||||
<ButtonStyled v-else color="brand">
|
||||
<button
|
||||
class="!shadow-none"
|
||||
@click="downloadFilesWithDependencies"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="downloadingActionType === 'dependencies'"
|
||||
aria-hidden="true"
|
||||
class="animate-spin"
|
||||
/>
|
||||
<DownloadIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.downloadWithDependencies) }}
|
||||
</SplitButton>
|
||||
<Button type="colored" color="brand" v-else
|
||||
:disabled="!!downloadingActionType || !dependencyDownloadFilesLoaded"
|
||||
@click="downloadFilesWithDependencies"
|
||||
>
|
||||
<SpinnerIcon
|
||||
v-if="downloadingActionType === 'dependencies'"
|
||||
aria-hidden="true"
|
||||
class="animate-spin"
|
||||
/>
|
||||
<DownloadIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.downloadWithDependencies) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<SpinnerIcon
|
||||
v-if="downloadingActionType === 'dependencies'"
|
||||
aria-hidden="true"
|
||||
class="animate-spin"
|
||||
/>
|
||||
<DownloadIcon v-else aria-hidden="true" />
|
||||
{{ formatMessage(messages.downloadWithDependencies) }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, type OverflowMenuOption, SplitButton } from '@modrinth/ui'
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { DownloadIcon, SpinnerIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
type CdnDownloadReason,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
type JoinedButtonAction,
|
||||
JoinedButtons,
|
||||
NewModal,
|
||||
ServersPromo,
|
||||
truncatedTooltip,
|
||||
@@ -342,13 +345,7 @@ const showDependencyDownloadActions = computed(
|
||||
hasRecommendedDownloadFiles.value),
|
||||
)
|
||||
|
||||
const downloadWithRecommendedActions = computed<JoinedButtonAction[]>(() => [
|
||||
{
|
||||
id: 'download-with-dependencies',
|
||||
label: formatMessage(messages.downloadWithDependencies),
|
||||
icon: downloadingActionType.value === 'dependencies' ? SpinnerIcon : DownloadIcon,
|
||||
action: () => void downloadFilesWithDependencies(),
|
||||
},
|
||||
const downloadWithRecommendedOptions = computed<OverflowMenuOption[]>(() => [
|
||||
{
|
||||
id: 'download-with-recommended',
|
||||
label: formatMessage(messages.downloadWithRecommended),
|
||||
|
||||
@@ -6,26 +6,22 @@
|
||||
</p>
|
||||
<p v-else>{{ getFormattedMessage(messages.invitationNoRole) }}</p>
|
||||
<div class="input-group">
|
||||
<ButtonStyled color="brand">
|
||||
<button class="brand-button" @click="acceptInvite()">
|
||||
<CheckIcon />
|
||||
{{ getFormattedMessage(commonMessages.acceptButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red">
|
||||
<button @click="declineInvite">
|
||||
<XIcon />
|
||||
{{ getFormattedMessage(commonMessages.declineButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" class="brand-button" @click="acceptInvite()">
|
||||
<CheckIcon />
|
||||
{{ getFormattedMessage(commonMessages.acceptButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="red" @click="declineInvite">
|
||||
<XIcon />
|
||||
{{ getFormattedMessage(commonMessages.declineButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { CheckIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
|
||||
@@ -1,62 +1,53 @@
|
||||
<template>
|
||||
<div class="flex gap-2">
|
||||
<ButtonStyled circular>
|
||||
<a
|
||||
<ButtonLink
|
||||
v-tooltip="`Share on Bluesky`"
|
||||
:href="`https://bsky.app/intent/compose?text=${encodedUrl}`"
|
||||
target="_blank"
|
||||
>
|
||||
<BlueskyIcon />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular>
|
||||
<a
|
||||
class="!w-9 !px-0 !rounded-full">
|
||||
<BlueskyIcon />
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
v-tooltip="`Share on Mastodon`"
|
||||
:href="`https://tootpick.org/#text=${encodedUrl}`"
|
||||
target="_blank"
|
||||
>
|
||||
<MastodonIcon />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular>
|
||||
<a
|
||||
class="!w-9 !px-0 !rounded-full">
|
||||
<MastodonIcon />
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
v-tooltip="`Share on X`"
|
||||
:href="`https://www.x.com/intent/post?url=${encodedUrl}`"
|
||||
target="_blank"
|
||||
>
|
||||
<TwitterIcon />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular>
|
||||
<a
|
||||
class="!w-9 !px-0 !rounded-full">
|
||||
<TwitterIcon />
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
v-tooltip="`Share via email`"
|
||||
:href="`mailto:${encodedTitle ? `?subject=${encodedTitle}&` : `?`}body=${encodedUrl}`"
|
||||
target="_blank"
|
||||
>
|
||||
<MailIcon />
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular>
|
||||
<button
|
||||
class="!w-9 !px-0 !rounded-full">
|
||||
<MailIcon />
|
||||
</ButtonLink>
|
||||
<IconButton :label="copied ? `Copied to clipboard` : `Copy link`"
|
||||
v-tooltip="copied ? `Copied to clipboard` : `Copy link`"
|
||||
:disabled="copied"
|
||||
class="relative grid place-items-center overflow-hidden"
|
||||
@click="copyToClipboard(url)"
|
||||
>
|
||||
<CheckIcon
|
||||
class="absolute transition-all ease-in-out"
|
||||
:class="copied ? 'translate-y-0' : 'translate-y-7'"
|
||||
/>
|
||||
<LinkIcon
|
||||
class="absolute transition-all ease-in-out"
|
||||
:class="copied ? '-translate-y-7' : 'translate-y-0'"
|
||||
/>
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<CheckIcon
|
||||
class="absolute transition-all ease-in-out"
|
||||
:class="copied ? 'translate-y-0' : 'translate-y-7'"
|
||||
/>
|
||||
<LinkIcon
|
||||
class="absolute transition-all ease-in-out"
|
||||
:class="copied ? '-translate-y-7' : 'translate-y-0'"
|
||||
/>
|
||||
</IconButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { IconButton, ButtonLink } from '@modrinth/ui'
|
||||
import {
|
||||
BlueskyIcon,
|
||||
CheckIcon,
|
||||
@@ -65,7 +56,7 @@ import {
|
||||
MastodonIcon,
|
||||
TwitterIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { ButtonStyled } from '@modrinth/ui'
|
||||
|
||||
|
||||
const props = defineProps<{
|
||||
title?: string
|
||||
|
||||
@@ -102,23 +102,19 @@
|
||||
formatMessage(messages.openingAutomatically)
|
||||
}}</span>
|
||||
<div v-else class="grid w-full grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<ButtonStyled>
|
||||
<button class="flex-1" type="button" @click="hide">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.closeButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<a
|
||||
<Button class="flex-1" native-type="button" @click="hide">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.closeButton) }}
|
||||
</Button>
|
||||
<ButtonLink type="colored" color="brand"
|
||||
class="flex-1"
|
||||
href="https://modrinth.com/app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<DownloadIcon />
|
||||
{{ formatMessage(messages.getApp) }}
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<DownloadIcon />
|
||||
{{ formatMessage(messages.getApp) }}
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -126,8 +122,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, ButtonLink } from '@modrinth/ui'
|
||||
import { CheckIcon, DownloadIcon, XIcon } from '@modrinth/assets'
|
||||
import { Avatar, ButtonStyled, commonMessages, defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { Avatar, commonMessages, defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { computed, nextTick, onUnmounted, ref } from 'vue'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
@@ -87,37 +87,24 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<ButtonStyled v-if="isRefunded">
|
||||
<div class="button-like disabled"><CheckIcon /> Charge refunded</div>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled
|
||||
v-else-if="charge.status === 'succeeded' && charge.type !== 'refund'"
|
||||
color="red"
|
||||
color-fill="text"
|
||||
>
|
||||
<button @click="emit('refund', charge)">
|
||||
<CurrencyIcon />
|
||||
Refund options
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled
|
||||
v-else-if="charge.status === 'failed' || charge.status === 'open'"
|
||||
color="red"
|
||||
color-fill="text"
|
||||
>
|
||||
<button @click="emit('modify', charge, subscription)">
|
||||
<CurrencyIcon />
|
||||
Modify charge
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button v-if="isRefunded" disabled><CheckIcon /> Charge refunded</Button>
|
||||
<Button v-else-if="charge.status === 'succeeded' && charge.type !== 'refund'" @click="emit('refund', charge)" class="!text-red [&>svg]:!text-red">
|
||||
<CurrencyIcon />
|
||||
Refund options
|
||||
</Button>
|
||||
<Button v-else-if="charge.status === 'failed' || charge.status === 'open'" @click="emit('modify', charge, subscription)" class="!text-red [&>svg]:!text-red">
|
||||
<CurrencyIcon />
|
||||
Modify charge
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import { CheckIcon, CurrencyIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, useFormatDateTime, useFormatPrice, useRelativeTime } from '@modrinth/ui'
|
||||
import { useFormatDateTime, useFormatPrice, useRelativeTime } from '@modrinth/ui'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import { products } from '~/generated/state.json'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import type { Archon } from '@modrinth/api-client'
|
||||
import { PlusIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Accordion,
|
||||
ButtonStyled,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
NewModal,
|
||||
@@ -181,24 +181,18 @@ defineExpose({ show, hide })
|
||||
wrapper-class="w-full"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<ButtonStyled color="green" color-fill="text">
|
||||
<button class="shrink-0" @click="() => assign(true)">
|
||||
<PlusIcon />
|
||||
Add server
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="blue" color-fill="text">
|
||||
<button class="shrink-0" @click="() => assign(false)">
|
||||
<PlusIcon />
|
||||
Add node
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="red" color-fill="text">
|
||||
<button class="shrink-0" @click="() => unassignDetect()">
|
||||
<XIcon />
|
||||
Remove
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" color="green" class="shrink-0 !text-green [&>svg]:!text-green" @click="() => assign(true)">
|
||||
<PlusIcon />
|
||||
Add server
|
||||
</Button>
|
||||
<Button type="quiet" color="blue" class="shrink-0 !text-blue [&>svg]:!text-blue" @click="() => assign(false)">
|
||||
<PlusIcon />
|
||||
Add node
|
||||
</Button>
|
||||
<Button type="quiet" color="red" class="shrink-0 !text-red [&>svg]:!text-red" @click="() => unassignDetect()">
|
||||
<XIcon />
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -42,12 +42,10 @@
|
||||
wrapper-class="w-32"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<ButtonStyled color="blue" color-fill="text">
|
||||
<button class="shrink-0" @click="addNode">
|
||||
<PlusIcon />
|
||||
Add
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" color="blue" class="shrink-0 !text-blue [&>svg]:!text-blue" @click="addNode">
|
||||
<PlusIcon />
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
<div v-if="selectedNodes.length" class="mt-1 flex flex-wrap gap-2">
|
||||
<TagItem v-for="h in selectedNodes" :key="`node-${h}`" :action="() => removeNode(h)">
|
||||
@@ -111,27 +109,23 @@
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="applyDisabled" @click="apply">
|
||||
<CheckIcon aria-hidden="true" />
|
||||
Apply credits
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="modal?.hide?.()">
|
||||
<XIcon aria-hidden="true" />
|
||||
Cancel
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" :disabled="applyDisabled" @click="apply">
|
||||
<CheckIcon aria-hidden="true" />
|
||||
Apply credits
|
||||
</Button>
|
||||
<Button @click="modal?.hide?.()">
|
||||
<XIcon aria-hidden="true" />
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { CheckIcon, PlusIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Combobox,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
|
||||
@@ -56,12 +56,10 @@
|
||||
placeholder="us-vin200, us-vin201"
|
||||
@keydown.enter.prevent="addNodes"
|
||||
/>
|
||||
<ButtonStyled color="blue" color-fill="text">
|
||||
<button class="shrink-0" @click="addNodes">
|
||||
<PlusIcon />
|
||||
Add
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" color="blue" class="shrink-0 !text-blue [&>svg]:!text-blue" @click="addNodes">
|
||||
<PlusIcon />
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
<div v-if="selectedNodes.length" class="mt-1 flex flex-wrap gap-2">
|
||||
<TagItem v-for="h in selectedNodes" :key="`node-${h}`" :action="() => removeNode(h)">
|
||||
@@ -122,12 +120,10 @@
|
||||
placeholder="ovh-gen4"
|
||||
@keydown.enter.prevent="addTag"
|
||||
/>
|
||||
<ButtonStyled color="blue" color-fill="text">
|
||||
<button class="shrink-0" @click="addTag">
|
||||
<PlusIcon />
|
||||
Add
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="quiet" color="blue" class="shrink-0 !text-blue [&>svg]:!text-blue" @click="addTag">
|
||||
<PlusIcon />
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
<div v-if="selectedTags.length" class="mt-1 flex flex-wrap gap-2">
|
||||
<TagItem v-for="t in selectedTags" :key="`tag-${t}`" :action="() => removeTag(t)">
|
||||
@@ -175,27 +171,23 @@
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="submitDisabled || submitting" @click="submit">
|
||||
<SendIcon aria-hidden="true" />
|
||||
{{ submitting ? 'Scheduling...' : 'Schedule transfer' }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="modal?.hide?.()">
|
||||
<XIcon aria-hidden="true" />
|
||||
Cancel
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" :disabled="submitDisabled || submitting" @click="submit">
|
||||
<SendIcon aria-hidden="true" />
|
||||
{{ submitting ? 'Scheduling...' : 'Schedule transfer' }}
|
||||
</Button>
|
||||
<Button @click="modal?.hide?.()">
|
||||
<XIcon aria-hidden="true" />
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</NewModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { PlusIcon, SendIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Chips,
|
||||
Combobox,
|
||||
injectModrinthClient,
|
||||
|
||||
@@ -84,24 +84,22 @@
|
||||
<label for="create-account-consent">I agree to receive account updates by email</label>
|
||||
</div>
|
||||
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
<Button type="colored" color="brand"
|
||||
class="!w-full font-bold"
|
||||
:disabled="globals?.captcha_enabled ? !tokenModel : false"
|
||||
@click="onCompleteSignUpClick"
|
||||
>
|
||||
{{ formatMessage(messages.completeSignUpButton) }}
|
||||
<RightArrowIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
{{ formatMessage(messages.completeSignUpButton) }}
|
||||
<RightArrowIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button } from '@modrinth/ui'
|
||||
import { RightArrowIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Admonition,
|
||||
ButtonStyled,
|
||||
Checkbox,
|
||||
DatePicker,
|
||||
defineMessages,
|
||||
|
||||
@@ -55,8 +55,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<ButtonStyled circular>
|
||||
<button
|
||||
<IconButton :label="formatMessage(commonMessages.renameButton)"
|
||||
v-tooltip="formatMessage(commonMessages.renameButton)"
|
||||
@click="
|
||||
() => {
|
||||
@@ -65,11 +64,9 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<EditIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled circular>
|
||||
<button
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
<IconButton :label="formatMessage(messages.deletePasskeyButton)"
|
||||
v-tooltip="formatMessage(messages.deletePasskeyButton)"
|
||||
@click="
|
||||
() => {
|
||||
@@ -78,25 +75,20 @@
|
||||
}
|
||||
"
|
||||
>
|
||||
<TrashIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<TrashIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="input-group self-end">
|
||||
<ButtonStyled>
|
||||
<button @click="registerPasskey()">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.managePasskeyAddPasskeyButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<button @click="managePasskeyModal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.closeButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="registerPasskey()">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.managePasskeyAddPasskeyButton) }}
|
||||
</Button>
|
||||
<Button @click="managePasskeyModal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.closeButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</NewModal>
|
||||
@@ -124,18 +116,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end gap-2.5">
|
||||
<ButtonStyled>
|
||||
<button @click="addPasskeyModal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="!pendingPasskeyName" @click="finishRegisterPasskey()">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.managePasskeyAddPasskeyButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="addPasskeyModal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" :disabled="!pendingPasskeyName" @click="finishRegisterPasskey()">
|
||||
<PlusIcon />
|
||||
{{ formatMessage(messages.managePasskeyAddPasskeyButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</NewModal>
|
||||
@@ -163,18 +151,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end gap-2.5">
|
||||
<ButtonStyled>
|
||||
<button @click="renamePasskeyModal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled color="brand">
|
||||
<button :disabled="!passkeyToRenameName" @click="renamePasskey()">
|
||||
<SaveIcon />
|
||||
{{ formatMessage(commonMessages.saveButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button @click="renamePasskeyModal?.hide()">
|
||||
<XIcon />
|
||||
{{ formatMessage(commonMessages.cancelButton) }}
|
||||
</Button>
|
||||
<Button type="colored" color="brand" :disabled="!passkeyToRenameName" @click="renamePasskey()">
|
||||
<SaveIcon />
|
||||
{{ formatMessage(commonMessages.saveButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</NewModal>
|
||||
@@ -187,17 +171,16 @@
|
||||
}}</span>
|
||||
</label>
|
||||
<div>
|
||||
<ButtonStyled>
|
||||
<button id="manage-passkeys" @click="showPasskeyModal">
|
||||
<UserKeyIcon /> {{ formatMessage(messages.managePasskeyButton) }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button id="manage-passkeys" @click="showPasskeyModal">
|
||||
<UserKeyIcon /> {{ formatMessage(messages.managePasskeyButton) }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, IconButton } from '@modrinth/ui'
|
||||
import type { Labrinth } from '@modrinth/api-client'
|
||||
import {
|
||||
EditIcon,
|
||||
@@ -209,7 +192,6 @@ import {
|
||||
XIcon,
|
||||
} from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
commonMessages,
|
||||
ConfirmModal,
|
||||
defineMessages,
|
||||
|
||||
@@ -28,11 +28,9 @@
|
||||
@keyup.enter="onTwoFactorSignIn()"
|
||||
/>
|
||||
</div>
|
||||
<ButtonStyled color="brand">
|
||||
<button @click="onTwoFactorSignIn()">
|
||||
{{ formatMessage(commonMessages.signInButton) }} <RightArrowIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
<Button type="colored" color="brand" @click="onTwoFactorSignIn()">
|
||||
{{ formatMessage(commonMessages.signInButton) }} <RightArrowIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
@@ -42,9 +40,8 @@
|
||||
</div>
|
||||
|
||||
<section class="grid grid-cols-1 gap-2.5 sm:grid-cols-2">
|
||||
<ButtonStyled v-for="provider in oauthProviders" :key="provider.id">
|
||||
<a
|
||||
class="relative w-full !justify-center overflow-visible !shadow-none"
|
||||
<ButtonLink v-for="provider in oauthProviders" :key="provider.id"
|
||||
class="relative w-full !justify-center overflow-visible"
|
||||
:class="{
|
||||
'!border !border-[var(--color-green)]': lastSignInProvider === provider.id,
|
||||
}"
|
||||
@@ -54,35 +51,32 @@
|
||||
"
|
||||
@click="onOAuthProviderClick(provider.id)"
|
||||
>
|
||||
<component :is="provider.icon" />
|
||||
<span>{{ provider.name }}</span>
|
||||
<span
|
||||
v-if="lastSignInProvider === provider.id"
|
||||
class="oauth-provider-last-sign-in-badge"
|
||||
>
|
||||
{{ formatMessage(messages.lastSignInLabel) }}
|
||||
</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<ButtonStyled>
|
||||
<a
|
||||
class="relative w-full !justify-center overflow-visible !shadow-none sm:col-span-2"
|
||||
<component :is="provider.icon" />
|
||||
<span>{{ provider.name }}</span>
|
||||
<span
|
||||
v-if="lastSignInProvider === provider.id"
|
||||
class="oauth-provider-last-sign-in-badge"
|
||||
>
|
||||
{{ formatMessage(messages.lastSignInLabel) }}
|
||||
</span>
|
||||
</ButtonLink>
|
||||
<Button
|
||||
class="relative w-full !justify-center overflow-visible sm:col-span-2"
|
||||
:class="{ '!border !border-[var(--color-green)]': lastSignInProvider === 'passkey' }"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="onPasskeySignIn"
|
||||
@keydown.enter="onPasskeySignIn"
|
||||
>
|
||||
<UserKeyIcon />
|
||||
<span class="ml-1">{{ formatMessage(messages.continueWithPasskey) }}</span>
|
||||
<span
|
||||
v-if="lastSignInProvider === 'passkey'"
|
||||
class="oauth-provider-last-sign-in-badge"
|
||||
>
|
||||
{{ formatMessage(messages.lastSignInLabel) }}
|
||||
</span>
|
||||
</a>
|
||||
</ButtonStyled>
|
||||
<UserKeyIcon />
|
||||
<span class="ml-1">{{ formatMessage(messages.continueWithPasskey) }}</span>
|
||||
<span
|
||||
v-if="lastSignInProvider === 'passkey'"
|
||||
class="oauth-provider-last-sign-in-badge"
|
||||
>
|
||||
{{ formatMessage(messages.lastSignInLabel) }}
|
||||
</span>
|
||||
</Button>
|
||||
</section>
|
||||
|
||||
<div class="h-px w-full bg-surface-5"></div>
|
||||
@@ -117,15 +111,13 @@
|
||||
v-model="tokenModel"
|
||||
/>
|
||||
|
||||
<ButtonStyled color="brand">
|
||||
<button
|
||||
<Button type="colored" color="brand"
|
||||
class="!w-full"
|
||||
:disabled="globals?.captcha_enabled ? !tokenModel : false"
|
||||
@click="onPasswordSignIn()"
|
||||
>
|
||||
{{ formatMessage(messages.continueWithEmail) }} <RightArrowIcon />
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
{{ formatMessage(messages.continueWithEmail) }} <RightArrowIcon />
|
||||
</Button>
|
||||
|
||||
<div class="flex flex-wrap items-center justify-center gap-2.5 !text-base">
|
||||
<NuxtLink
|
||||
@@ -155,6 +147,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Button, ButtonLink } from '@modrinth/ui'
|
||||
import {
|
||||
DiscordColorIcon,
|
||||
GitHubColorIcon,
|
||||
@@ -167,7 +160,7 @@ import {
|
||||
SteamColorIcon,
|
||||
UserKeyIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { ButtonStyled, commonMessages, defineMessages, StyledInput, useVIntl } from '@modrinth/ui'
|
||||
import { commonMessages, defineMessages, StyledInput, useVIntl } from '@modrinth/ui'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import { computed } from 'vue'
|
||||
import type { LocationQuery } from 'vue-router'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user