Skip to content
Draft
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
8 changes: 6 additions & 2 deletions apps/app-frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2216,7 +2216,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 +2548,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 +2557,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
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ export function gameSettingChanges(
})
}

export function gameSettingNumberScale(setting: EditableGameSetting): number {
if (setting.editor.unit !== 'percent') return 1
return setting.option_id === 'sensitivity' ? 200 : 100
}

export function canonicalValueText(setting: EditableGameSetting): string {
const value = setting.canonical_value
if (!value) return ''
Expand All @@ -74,7 +79,7 @@ export function canonicalValueText(setting: EditableGameSetting): string {
case 'integer':
case 'decimal':
return setting.editor.unit === 'percent'
? String(Number((Number(value.value) * 100).toFixed(8)))
? String(Number((Number(value.value) * gameSettingNumberScale(setting)).toFixed(8)))
: String(value.value)
case 'string_list':
return value.value.join(', ')
Expand All @@ -90,7 +95,7 @@ export function canonicalBooleanValue(setting: EditableGameSetting): boolean | u
export function isKeybindSetting(setting: EditableGameSetting): boolean {
return (
setting.editor.type === 'key_binding' ||
(setting.editor.type === 'external_raw' && !!setting.raw_key?.startsWith('key_key'))
(setting.editor.type === 'external_raw' && !!setting.raw_key?.startsWith('key_'))
)
}

Expand All @@ -114,7 +119,7 @@ export function canonicalValueFromInput(
type: 'decimal',
value:
setting.editor.unit === 'percent'
? String(Number((parsed / 100).toFixed(8)))
? String(Number((parsed / gameSettingNumberScale(setting)).toFixed(8)))
: String(value),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,10 @@ import {
settingSearchText,
} from './editors'
import { minecraftKeybindConflictKey } from './keybinds'
import {
formatGameSettingDescription,
formatGameSettingLabel,
gameSettingCategoryMessage,
} from './messages'
import { formatGameSettingDescription, gameSettingCategoryMessage } from './messages'
import GameSettingRow from './row.vue'
import { useGameSettingsEditor } from './use-editor'
import { useGameSettingLabels } from './use-labels'

const props = defineProps<{
instanceId?: string
Expand Down Expand Up @@ -119,6 +116,7 @@ const modal = ref<InstanceType<typeof TabbedModal> | null>(null)
const confirmLeaveModal = ref<InstanceType<typeof ConfirmLeaveModal> | null>(null)
const activeCategoryId = ref('')
const search = ref('')
const opened = ref(false)
let allowClose = false

const {
Expand Down Expand Up @@ -154,6 +152,16 @@ const categoryIcons: Record<string, Component> = {
custom_settings: WrenchIcon,
}

const localeLabels = useGameSettingLabels(
opened,
() => props.instanceId,
() => draftState.value?.settings ?? [],
)

function settingLabel(setting: EditableGameSetting) {
return localeLabels.value[setting.option_id]?.label ?? setting.raw_key ?? setting.option_id
}

const categories = computed<GameSettingCategory[]>(() => {
if (!draftState.value) return []

Expand Down Expand Up @@ -192,7 +200,7 @@ const categorySettings = computed(() => {
query &&
!settingSearchText(
setting,
formatGameSettingLabel(formatMessage, setting),
settingLabel(setting),
formatGameSettingDescription(formatMessage, setting),
).includes(query)
)
Expand Down Expand Up @@ -220,7 +228,7 @@ const keybindConflicts = computed(() => {
setting.option_id,
settings
.filter((candidate) => candidate.option_id !== setting.option_id)
.map((candidate) => formatGameSettingLabel(formatMessage, candidate)),
.map((candidate) => settingLabel(candidate)),
)
}
}
Expand Down Expand Up @@ -267,6 +275,7 @@ async function load() {
}

function show() {
opened.value = true
allowClose = false
search.value = ''
modal.value?.show()
Expand All @@ -278,6 +287,7 @@ function hide() {
}

function reset() {
opened.value = false
resetEditor()
allowClose = false
}
Expand Down Expand Up @@ -394,6 +404,7 @@ defineExpose({ show, hide })
v-for="setting in categorySettings"
:key="setting.option_id"
:setting="setting"
:locale-label="localeLabels[setting.option_id]"
:keybind-conflicts="keybindConflicts.get(setting.option_id)"
:show-sync-toggle="!isLocalEditor"
@update:sync-enabled="setSyncEnabled([setting.option_id], $event)"
Expand Down
Loading
Loading