Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions apps/app-frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,6 @@ window.addEventListener('online', () => {
offline.value = false
})

const nativeDecorations = ref(false)

const os = ref('')
const isDevEnvironment = ref(false)

Expand Down Expand Up @@ -760,7 +758,7 @@ async function setupApp() {
const dev = await isDev()
isDevEnvironment.value = dev
const version = await getVersion()
nativeDecorations.value = native_decorations
appSettings.nativeDecorations = native_decorations
if (os.value !== 'MacOS') await getCurrentWindow().setDecorations(native_decorations)

appTheme.preferred = theme
Expand Down Expand Up @@ -2216,7 +2214,6 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
>
<PlusIcon />
</NavButton>
<div class="flex flex-grow"></div>
<NavButton
v-tooltip.right="formatMessage(commonMessages.settingsLabel)"
:to="() => appSettingsModal?.show()"
Expand Down Expand Up @@ -2549,7 +2546,7 @@ provideAppUpdateDownloadProgress(appUpdateDownload)
display: grid;
grid-template: 'status status' 'nav dummy';
grid-template-columns: auto 1fr;
grid-template-rows: auto 1fr;
grid-template-rows: auto minmax(0, 1fr);
position: relative;
//z-index: 0;
background-color: var(--color-raised-bg);
Expand All @@ -2558,8 +2555,13 @@ provideAppUpdateDownloadProgress(appUpdateDownload)

.app-grid-navbar {
grid-area: nav;
min-height: 0;
position: relative;
z-index: 2;

> :deep(*) {
flex-shrink: 0;
}
}

.app-grid-statusbar {
Expand Down
114 changes: 63 additions & 51 deletions apps/app-frontend/src/components/ui/QuickInstanceSwitcher.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import { showInstanceInFolder } from '@/helpers/utils'
import { instanceListQueryOptions } from '@/pages/instance/query-options'

const ITEM_SIZE = 52
const APPROX_USED_VERTICAL_SPACE = 475 // doesn't need to be exact lol just close enough so there's a little gap and no overflow
const { handleError } = injectNotificationManager()
const instancesQuery = useQuery(instanceListQueryOptions())
const router = useRouter()
Expand All @@ -36,6 +35,8 @@ const runningInstances = ref([])

const { formatMessage } = useVIntl()

const container = ref()
let resizeObserver
const maxAuto = ref(0)
const allInstances = computed(() =>
(instancesQuery.data.value ?? []).slice().sort((a, b) => {
Expand Down Expand Up @@ -69,9 +70,13 @@ const canDrag = computed(() => maxVisible.value > 0)
const showOverdrag = ref(false)

const updateMaxAuto = () => {
if (!container.value) return
const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize)
const dividerHeight = rem + 1
const gap = rem / 4
maxAuto.value = Math.max(
0,
Math.floor((window.innerHeight - APPROX_USED_VERTICAL_SPACE) / ITEM_SIZE),
Math.floor((container.value.clientHeight - 2 * dividerHeight - gap) / (3 * rem + gap)),
)
}

Expand Down Expand Up @@ -154,17 +159,18 @@ const onDividerPointerUp = (event) => {
}

await instancesQuery.suspense().catch(handleError)
updateMaxAuto()

useAppEvent('process', checkProcesses)

onMounted(() => {
window.addEventListener('resize', updateMaxAuto)
resizeObserver = new ResizeObserver(updateMaxAuto)
resizeObserver.observe(container.value)
updateMaxAuto()
checkProcesses()
})

onUnmounted(() => {
window.removeEventListener('resize', updateMaxAuto)
resizeObserver?.disconnect()
document.body.classList.remove('quick-instance-dragging')
clearOverdragFlash()
})
Expand Down Expand Up @@ -264,55 +270,61 @@ function openContextMenu(event, instance) {
</script>

<template>
<Transition name="top-divider">
<div ref="container" class="flex min-h-0 flex-1 flex-col gap-1">
<Transition name="top-divider">
<div
v-if="recentInstances.length > 0"
class="top-divider shrink-0 flex items-center justify-center overflow-hidden"
>
<div class="h-px w-8 bg-surface-5 shrink-0"></div>
</div>
</Transition>
<TransitionGroup name="quick-instance" tag="div" class="flex shrink-0 flex-col items-center">
<div
v-for="instance in recentInstances"
:key="instance.id"
v-tooltip.right="instance.name"
class="quick-instance-item"
@contextmenu.prevent.stop="(event) => openContextMenu(event, instance)"
>
<NavButton :to="`/instance/${encodeURIComponent(instance.id)}`" class="relative">
<Avatar
:src="getInstanceIconUrl(instance.icon_path)"
size="28px"
:tint-by="instance.id"
:class="`transition-all ${instance.install_stage !== 'installed' ? `brightness-[0.25] scale-[0.85]` : `group-hover:brightness-75`}`"
pad-transparent-corners
/>
<div
v-if="instance.install_stage !== 'installed'"
class="absolute inset-0 flex items-center justify-center z-10 pointer-events-none"
>
<SpinnerIcon class="animate-spin w-4 h-4" />
</div>
</NavButton>
</div>
</TransitionGroup>
<ContextMenu ref="instanceOptions" :label="formatMessage(messages.instanceActions)" />
<div
v-if="recentInstances.length > 0"
class="top-divider flex items-center justify-center overflow-hidden"
v-tooltip.right="dividerTooltip"
class="flex shrink-0 items-center justify-center py-2 select-none"
:class="canDrag ? 'cursor-ns-resize touch-none group' : ''"
@pointerdown="onDividerPointerDown"
@pointermove="onDividerPointerMove"
@pointerup="onDividerPointerUp"
@pointercancel="onDividerPointerUp"
>
<div class="h-px w-8 bg-surface-5 shrink-0"></div>
<div
class="h-px w-8 transition-colors duration-200"
:class="
showOverdrag
? 'bg-red'
: canDrag
? 'bg-surface-5 group-hover:bg-secondary'
: 'bg-surface-5'
"
></div>
</div>
</Transition>
<TransitionGroup name="quick-instance" tag="div" class="flex flex-col items-center">
<div
v-for="instance in recentInstances"
:key="instance.id"
v-tooltip.right="instance.name"
class="quick-instance-item"
@contextmenu.prevent.stop="(event) => openContextMenu(event, instance)"
>
<NavButton :to="`/instance/${encodeURIComponent(instance.id)}`" class="relative">
<Avatar
:src="getInstanceIconUrl(instance.icon_path)"
size="28px"
:tint-by="instance.id"
:class="`transition-all ${instance.install_stage !== 'installed' ? `brightness-[0.25] scale-[0.85]` : `group-hover:brightness-75`}`"
pad-transparent-corners
/>
<div
v-if="instance.install_stage !== 'installed'"
class="absolute inset-0 flex items-center justify-center z-10 pointer-events-none"
>
<SpinnerIcon class="animate-spin w-4 h-4" />
</div>
</NavButton>
</div>
</TransitionGroup>
<ContextMenu ref="instanceOptions" :label="formatMessage(messages.instanceActions)" />
<div
v-tooltip.right="dividerTooltip"
class="flex items-center justify-center py-2 select-none"
:class="canDrag ? 'cursor-ns-resize touch-none group' : ''"
@pointerdown="onDividerPointerDown"
@pointermove="onDividerPointerMove"
@pointerup="onDividerPointerUp"
@pointercancel="onDividerPointerUp"
>
<div
class="h-px w-8 transition-colors duration-200"
:class="
showOverdrag ? 'bg-red' : canDrag ? 'bg-surface-5 group-hover:bg-secondary' : 'bg-surface-5'
"
></div>
</div>
</template>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ const cardWidth = computed(
() => (gridWidth.value - gap.value * (columnCount.value - 1)) / columnCount.value,
)
const cardHeight = computed(() =>
compactMode.value ? remSize.value * 3.875 : Math.max(0, cardWidth.value) + remSize.value * 3.375,
compactMode.value
? remSize.value * 3.875 + 2
: Math.max(0, cardWidth.value) + remSize.value * 3.375,
)
const gridHeight = computed(() =>
Math.max(
Expand Down
47 changes: 12 additions & 35 deletions apps/app-frontend/src/components/ui/modal/AppSettingsModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,19 @@ import {
} from '@modrinth/ui'
import { getVersion } from '@tauri-apps/api/app'
import { platform as getOsPlatform, version as getOsVersion } from '@tauri-apps/plugin-os'
import { computed, defineAsyncComponent, provide, ref, watch } from 'vue'
import { computed, provide, ref, watch } from 'vue'

import PrivacySettings from '@/components/ui/settings/account/PrivacySettings.vue'
import ProfileSettings from '@/components/ui/settings/account/ProfileSettings.vue'
import SocialSettings from '@/components/ui/settings/account/SocialSettings.vue'
import AppearanceSettings from '@/components/ui/settings/display/AppearanceSettings.vue'
import BehaviorSettings from '@/components/ui/settings/display/BehaviorSettings.vue'
import FeatureFlagSettings from '@/components/ui/settings/display/FeatureFlagSettings.vue'
import FeaturesSettings from '@/components/ui/settings/display/FeaturesSettings.vue'
import LanguageSettings from '@/components/ui/settings/display/LanguageSettings.vue'
import InstancesSyncedSettings from '@/components/ui/settings/instances/instances-synced-settings/index.vue'
import JavaSettings from '@/components/ui/settings/instances/JavaSettings.vue'
import ResourceManagementSettings from '@/components/ui/settings/instances/ResourceManagementSettings.vue'
import { useAppSettings } from '@/composables/use-app-settings.ts'
import { get, set } from '@/helpers/settings.ts'
import {
Expand All @@ -35,40 +46,6 @@ import {
} from '@/providers/app-settings-modal'
import { injectAppUpdateDownloadProgress } from '@/providers/download-progress.ts'

const PrivacySettings = defineAsyncComponent(
() => import('@/components/ui/settings/account/PrivacySettings.vue'),
)
const ProfileSettings = defineAsyncComponent(
() => import('@/components/ui/settings/account/ProfileSettings.vue'),
)
const SocialSettings = defineAsyncComponent(
() => import('@/components/ui/settings/account/SocialSettings.vue'),
)
const AppearanceSettings = defineAsyncComponent(
() => import('@/components/ui/settings/display/AppearanceSettings.vue'),
)
const BehaviorSettings = defineAsyncComponent(
() => import('@/components/ui/settings/display/BehaviorSettings.vue'),
)
const FeatureFlagSettings = defineAsyncComponent(
() => import('@/components/ui/settings/display/FeatureFlagSettings.vue'),
)
const FeaturesSettings = defineAsyncComponent(
() => import('@/components/ui/settings/display/FeaturesSettings.vue'),
)
const LanguageSettings = defineAsyncComponent(
() => import('@/components/ui/settings/display/LanguageSettings.vue'),
)
const InstancesSyncedSettings = defineAsyncComponent(
() => import('@/components/ui/settings/instances/instances-synced-settings/index.vue'),
)
const JavaSettings = defineAsyncComponent(
() => import('@/components/ui/settings/instances/JavaSettings.vue'),
)
const ResourceManagementSettings = defineAsyncComponent(
() => import('@/components/ui/settings/instances/ResourceManagementSettings.vue'),
)

// TODO: Apply COMPONENT_STRUCTURE.md here and extract out common setting option components
const appSettings = useAppSettings()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@ import {
provideAppearanceSettings,
useSavable,
} from '@modrinth/ui'
import { computed, inject, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { platform } from '@tauri-apps/plugin-os'
import { computed, inject, onBeforeUnmount, onMounted, watch } from 'vue'

import { useAppSettings } from '@/composables/use-app-settings.ts'
import { type ColorTheme, isDarkTheme, useTheme } from '@/composables/use-theme.ts'
import { type AppSettings, get, set } from '@/helpers/settings.ts'
import { getOS } from '@/helpers/utils'
import { appSettingsModalContextKey } from '@/providers/app-settings-modal'

const theme = useTheme()
const appSettings = useAppSettings()
const auth = injectAuth()
const { updatePreferences } = injectUserPreferences()
const settingsModal = inject(appSettingsModalContextKey, null)
const os = await getOS()
const settings = ref(await get())
const os = platform()

type AppearanceSettingsState = {
theme: ColorTheme
Expand All @@ -27,17 +28,17 @@ type AppearanceSettingsState = {
nativeDecorations: boolean
}

function getAppearanceSettingsState(settings: AppSettings): AppearanceSettingsState {
function getAppearanceSettingsState(): AppearanceSettingsState {
return {
theme: settings.theme,
syncAcrossDevices: settings.sync_theme_across_devices,
advancedRendering: settings.advanced_rendering,
nativeDecorations: settings.native_decorations,
theme: theme.preferred,
syncAcrossDevices: theme.syncAcrossDevices,
advancedRendering: theme.advancedRendering,
nativeDecorations: appSettings.nativeDecorations,
}
}

const { saved, current, changes, saving, hasChanges, reset, save } = useSavable(
() => getAppearanceSettingsState(settings.value),
getAppearanceSettingsState,
async (appearanceChanges) => {
const value = current.value
if (
Expand All @@ -51,28 +52,27 @@ const { saved, current, changes, saving, hasChanges, reset, save } = useSavable(
}

const nextSettings: AppSettings = {
...settings.value,
...(await get()),
theme: value.theme,
sync_theme_across_devices: value.syncAcrossDevices,
advanced_rendering: value.advancedRendering,
native_decorations: value.nativeDecorations,
}

await set(nextSettings)
settings.value = nextSettings
if (isDarkTheme(value.theme)) {
theme.preferredDark = value.theme
}
theme.preferred = value.theme
theme.syncAcrossDevices = value.syncAcrossDevices
theme.advancedRendering = value.advancedRendering
appSettings.nativeDecorations = value.nativeDecorations
},
)

const themeOptions = computed(() =>
theme.options.filter(
(option) =>
option !== 'retro' || settings.value.developer_mode || current.value.theme === 'retro',
(option) => option !== 'retro' || appSettings.devMode || current.value.theme === 'retro',
),
)

Expand Down Expand Up @@ -147,7 +147,7 @@ provideAppearanceSettings({
set: setAdvancedRendering,
},
nativeDecorations:
os !== 'MacOS'
os !== 'macos'
? {
value: computed(() => current.value.nativeDecorations),
set: setNativeDecorations,
Expand Down
Loading
Loading