Change the way buttons and info items are colored, fix inconsistencies, change sidebar, add a new method of interacting instead of installs/launch button

This commit is contained in:
2025-12-20 14:48:37 -07:00
parent cc79eb6d4a
commit 19c1d0b214
10 changed files with 284 additions and 152 deletions

View File

@@ -65,11 +65,19 @@ body {
}
.button {
@apply bg-[#0a6ec8] hover:bg-[#1361ad] disabled:bg-[#124c7e] disabled:hover:bg-[#1b3f63] disabled:text-[#bdbdbd] disabled:hover:text-[#e6e6e6] rounded-md cursor-pointer text-[16px] py-1.5 px-3 transition-colors duration-[0.25s];
@apply rounded-md cursor-pointer text-[16px] py-1.5 px-3 transition-colors border;
}
.button-green {
@apply bg-[#28a745] hover:bg-[#218838] disabled:bg-[#1c7430] disabled:hover:bg-[#1a5c24] disabled:text-[#bdbdbd] disabled:hover:text-[#e6e6e6];
.btntheme1 {
@apply bg-(--col2) border-(--col4) hover:bg-(--col4) hover:border-(--col6);
}
.btntheme2 {
@apply bg-(--col3) border-(--col5) hover:bg-(--col5) hover:border-(--col7);
}
.btntheme3 {
@apply bg-(--col4) border-(--col6) hover:bg-(--col6) hover:border-(--col8);
}
::-webkit-scrollbar {
@@ -111,7 +119,7 @@ body {
}
.popup-box {
@apply relative w-[600px] h-[480px] rounded-lg bg-(--col1) border border-(--col3) flex flex-col p-6;
@apply relative w-150 h-120 rounded-lg bg-(--col1) border border-(--col3) flex flex-col p-6;
}
.popup-content {
@@ -139,13 +147,5 @@ body {
}
.entry-info-item {
@apply flex flex-row items-center gap-1 bg-(--col1) border border-(--col3) text-gray-300 py-1 px-2 rounded-lg w-fit text-[16px] transition-colors cursor-pointer;
}
.downloads-entry:hover .entry-info-item {
@apply bg-(--col2) border-(--col4);
}
.popup-content .entry-info-item:hover {
@apply bg-(--col3) border-(--col5);
@apply flex flex-row items-center gap-1 border text-gray-300 py-1 px-2 rounded-lg w-fit text-[16px] transition-colors cursor-pointer;
}

View File

@@ -9,9 +9,5 @@
}
.downloads-entry {
@apply flex justify-between items-center m-2 p-4 rounded-lg text-gray-200 text-lg transition-colors cursor-default bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5);
}
.downloads-entry p.score {
@apply font-mono text-blue-500 text-lg;
@apply flex justify-between items-center m-2 p-4 rounded-lg text-gray-200 text-lg transition-colors bg-(--col2) hover:bg-(--col3) border border-(--col4) hover:border-(--col5);
}

View File

@@ -5,20 +5,20 @@
}
.sidebar-downloads {
@apply text-[#bdbdbd] fixed bottom-3 left-2 bg-(--col2) rounded-lg border border-(--col3) w-55 p-4 cursor-pointer transition-colors duration-[0.25s];
@apply text-[#bdbdbd] fixed bottom-2 left-2 bg-(--col2) rounded-lg border border-(--col3) px-4 py-3 text-xl cursor-pointer transition-colors duration-[0.25s] opacity-75;
}
.sidebar-downloads:hover {
@apply text-white;
@apply text-white opacity-100;
@apply bg-(--col3) border-(--col4);
}
.logo {
@apply text-2xl font-bold p-4;
@apply flex items-center pl-1 h-14 w-60;
}
.nav-links {
@apply flex flex-col p-4 space-y-1 overflow-y-auto overflow-x-hidden;
@apply flex flex-col px-4 space-y-1;
}
.link {
@@ -37,3 +37,11 @@
.link.active:hover {
@apply bg-(--col3) border-(--col4);
}
.sidebar ::-webkit-scrollbar {
@apply hidden;
}
.macos-drag {
@apply h-7 fixed top-0 left-0 w-60 -z-10;
}

View File

@@ -12,11 +12,11 @@ import {
} from '@fortawesome/free-solid-svg-icons'
import { faDiscord } from '@fortawesome/free-brands-svg-icons'
import { platform } from '@tauri-apps/plugin-os'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { useGlobal } from '../GlobalProvider'
import Image from 'next/image'
import Link from 'next/link'
import { usePathname, useSearchParams } from 'next/navigation'
import { getCurrentWindow } from '@tauri-apps/api/window'
export default function Sidebar () {
const {
@@ -34,34 +34,41 @@ export default function Sidebar () {
return (
<aside className='sidebar'>
<div
data-tauri-drag-region
style={{
height: '30px',
width: 'calc(var(--spacing) * 60)',
top: 0,
left: 0,
marginBottom: '-15px',
position: 'absolute',
zIndex: 9999,
display: platform() == 'macos' ? 'block' : 'none',
pointerEvents: 'auto'
className='macos-drag'
hidden={platform() != 'macos'}
onMouseDown={e => {
if (e.buttons === 1) {
e.detail === 2
? getCurrentWindow().toggleMaximize()
: getCurrentWindow().startDragging()
}
}}
></div>
<div className='logo'>
<Image
draggable={false}
src={Icon}
width={48}
height={48}
alt=''
<div
className='logo'
style={{
marginTop: ['windows', 'macos'].includes(platform())
? '20px'
: '0px',
marginBottom: '-20px'
marginTop:
platform() == 'windows'
? '32px'
: platform() == 'macos'
? '28px'
: ''
}}
/>
onMouseDown={e => {
if (platform() != 'macos') return
if (e.buttons === 1) {
e.detail === 2
? getCurrentWindow().toggleMaximize()
: getCurrentWindow().startDragging()
}
}}
>
<Image draggable={false} src={Icon} width={48} height={48} alt='' />
<p className='ml-1 text-[17px] whitespace-nowrap'>
Lncvrt Games Launcher
</p>
</div>
<div className='overflow-auto'>
<nav className='nav-links'>
<Link
draggable={false}
@@ -93,10 +100,10 @@ export default function Sidebar () {
: 'hidden'
}`}
>
<FontAwesomeIcon icon={faGamepad} className='mr-1' />{' '}
{i.cutOff == null
? i.name
: i.name.substring(0, i.cutOff) + '...'}
<div className='flex items-center'>
<FontAwesomeIcon icon={faGamepad} className='mr-1' />
<span className='truncate max-w-full'>{i.name}</span>
</div>
</Link>
))}
<Link
@@ -106,17 +113,17 @@ export default function Sidebar () {
>
<FontAwesomeIcon icon={faCog} className='mr-1' /> Settings
</Link>
<a
draggable={false}
<button
onClick={() => openUrl('https://games.lncvrt.xyz/discord')}
className='link'
className='link mr-auto'
>
<FontAwesomeIcon icon={faDiscord} className='mr-1' /> Community
</a>
</button>
</nav>
</div>
<div
className='sidebar-downloads'
style={{ display: downloadProgress.length != 0 ? 'block' : 'none' }}
hidden={downloadProgress.length == 0}
onClick={() => {
setPopupMode(1)
setShowPopup(true)

View File

@@ -44,7 +44,10 @@ export default function VersionInfo () {
{getVersionInfo(managingVersion)?.versionName}
</p>
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
<div className='entry-info-item' hidden={viewingInfoFromDownloads}>
<div
className='entry-info-item btntheme2'
hidden={viewingInfoFromDownloads}
>
<p>
Installed{' '}
{format(
@@ -56,7 +59,7 @@ export default function VersionInfo () {
</p>
</div>
<div
className='entry-info-item'
className='entry-info-item btntheme2'
hidden={!versionInfo || versionInfo.releaseDate == 0}
>
<p>
@@ -69,23 +72,23 @@ export default function VersionInfo () {
)}
</p>
</div>
<div className='entry-info-item' hidden={!gameInfo?.official}>
<div className='entry-info-item btntheme2' hidden={!gameInfo?.official}>
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
<p>Official</p>
</div>
<div className='entry-info-item' hidden={gameInfo?.official}>
<div className='entry-info-item btntheme2' hidden={gameInfo?.official}>
<FontAwesomeIcon
icon={gameInfo?.verified ? faShieldHalved : faWarning}
color={gameInfo?.verified ? '#19c84b' : '#ffc800'}
/>
<p>{gameInfo?.verified ? 'Verified' : 'Unverified'}</p>
</div>
<div className='entry-info-item' hidden={gameInfo?.official}>
<div className='entry-info-item btntheme2' hidden={gameInfo?.official}>
<FontAwesomeIcon icon={faCode} color='lightgray' />
<p>Developer: {gameInfo?.developer}</p>
</div>
<div
className='entry-info-item'
className='entry-info-item btntheme2'
hidden={viewingInfoFromDownloads || versionSize === null}
>
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
@@ -99,7 +102,7 @@ export default function VersionInfo () {
: 'Loading...'}
</p>
</div>
<div className='entry-info-item' hidden={!versionInfo}>
<div className='entry-info-item btntheme2' hidden={!versionInfo}>
<FontAwesomeIcon icon={faHardDrive} color='lightgray' />
<p>
Size when downloaded (zipped):{' '}

View File

@@ -1,6 +1,6 @@
'use client'
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import '../Installs.css'
import { format } from 'date-fns'
import { invoke } from '@tauri-apps/api/core'
@@ -25,6 +25,7 @@ export default function Installs () {
} = useGlobal()
const params = useSearchParams()
const [hoveredIds, setHoveredIds] = useState<string[]>([])
useEffect(() => {
if (!showPopup) return
@@ -36,7 +37,7 @@ export default function Installs () {
<div className='flex justify-between items-center mb-4'>
<p className='text-3xl'>Installs</p>
<button
className='button text-3xl'
className='button btntheme1 text-3xl'
onClick={() => {
setSelectedGame(Number(params.get('id') || 0))
setPopupMode(0)
@@ -56,7 +57,12 @@ export default function Installs () {
: 'h-[calc(100vh-84px)]'
}`}
>
{downloadedVersionsConfig && downloadedVersionsConfig.list.length ? (
{downloadedVersionsConfig &&
downloadedVersionsConfig.list.filter(v => {
const info = getVersionInfo(v)
if (!info) return false
return info.game === Number(params.get('id') || 0)
}).length != 0 ? (
downloadedVersionsConfig.list
.sort((a, b) => {
const infoA = getVersionInfo(a)
@@ -70,13 +76,38 @@ export default function Installs () {
return info.game === Number(params.get('id') || 0)
})
.map((entry, i) => (
<div key={i} className='downloads-entry'>
<div
key={entry}
className={`downloads-entry ${
normalConfig?.settings.useLegacyInteractButtons
? ''
: 'cursor-pointer'
}`}
title='Click to launch game'
onClick={async () => {
if (normalConfig?.settings.useLegacyInteractButtons) return
const verInfo = getVersionInfo(entry)
if (verInfo == undefined) return
invoke('launch_game', {
name: verInfo.id,
executable: verInfo.executable
})
}}
onMouseEnter={() => setHoveredIds(prev => [...prev, entry])}
onMouseLeave={() =>
setHoveredIds(prev => prev.filter(i => i !== i))
}
>
<div className='flex flex-col'>
<p className='text-2xl'>
{getVersionGame(getVersionInfo(entry)?.game)?.name} v
{getVersionInfo(entry)?.versionName}
</p>
<div className='entry-info-item'>
<div
className={`entry-info-item ${
hoveredIds.includes(entry) ? 'btntheme3' : 'btntheme2'
}`}
>
<p>
Installed{' '}
{format(
@@ -88,8 +119,11 @@ export default function Installs () {
</div>
<div className='flex flex-row items-center gap-2'>
<button
className='button'
onClick={async () => {
className={`button ${
hoveredIds.includes(entry) ? 'btntheme3' : 'btntheme2'
}`}
onClick={e => {
e.stopPropagation()
setManagingVersion(entry)
setPopupMode(3)
setShowPopup(true)
@@ -99,8 +133,11 @@ export default function Installs () {
View Info
</button>
<button
className='button'
onClick={async () => {
className={`button ${
hoveredIds.includes(entry) ? 'btntheme3' : 'btntheme2'
}`}
onClick={e => {
e.stopPropagation()
setManagingVersion(entry)
setPopupMode(2)
setShowPopup(true)
@@ -110,8 +147,11 @@ export default function Installs () {
Manage
</button>
<button
className='button button-green'
onClick={async () => {
className={`button ${
hoveredIds.includes(entry) ? 'btntheme3' : 'btntheme2'
}`}
onClick={e => {
e.stopPropagation()
const verInfo = getVersionInfo(entry)
if (verInfo == undefined) return
invoke('launch_game', {
@@ -119,6 +159,7 @@ export default function Installs () {
executable: verInfo.executable
})
}}
hidden={!normalConfig?.settings.useLegacyInteractButtons}
>
Launch
</button>
@@ -127,7 +168,7 @@ export default function Installs () {
))
) : (
<div className='flex justify-center items-center h-full'>
<p className='text-3xl'>No games installed</p>
<p className='text-3xl'>No versions installed</p>
</div>
)}
</div>

View File

@@ -212,6 +212,7 @@ export default function RootLayout ({
}, [])
useEffect(() => {
if (process.env.NODE_ENV !== 'production') return
const handler = (e: MouseEvent) => e.preventDefault()
document.addEventListener('contextmenu', handler)
return () => document.removeEventListener('contextmenu', handler)
@@ -447,13 +448,13 @@ export default function RootLayout ({
>
<Sidebar />
<div
className='relative z-2 ml-[239px] w-[calc(100vw-239px)] border-b border-(--col3) h-[33px] bg-(--col1)'
className='relative z-2 ml-59.75 w-[calc(100vw-239px)] border-b border-(--col3) h-8.25 bg-(--col1)'
style={{
display: platform() === 'windows' ? 'block' : 'none'
}}
/>
<div className='relative z-0'>
<main style={{ marginLeft: '15rem' }}>{children}</main>
<main className='ml-60'>{children}</main>
</div>
{showPopup && (
<div
@@ -500,17 +501,20 @@ export default function RootLayout ({
{getSpecialVersionsList(selectedGame).map(
(v, i) => (
<div key={i} className='popup-entry'>
<p className='text-2xl'>
{getVersionGame(v.game)?.cutOff == null
? getVersionGame(v.game)?.name
: getVersionGame(v.game)?.name.substring(
0,
getVersionGame(v.game)?.cutOff ?? 0
) + '...'}{' '}
v{v.versionName}
<div className='flex items-center'>
<p
className={`text-2xl truncate ${
selectedVersionList.includes(v.id)
? 'max-w-82.5'
: 'max-w-90'
}`}
>
{getVersionGame(v.game)?.name} v
{v.versionName}
</p>
</div>
<button
className='button right-22 bottom-1.5'
className='button btntheme3 right-22 bottom-1.5'
onClick={() => {
setSelectedVersionList(prev =>
prev.includes(v.id)
@@ -531,7 +535,7 @@ export default function RootLayout ({
)}
</button>
<button
className='button right-1.5 bottom-1.5'
className='button btntheme3 right-1.5 bottom-1.5'
onClick={() => {
setManagingVersion(v.id)
setViewingInfoFromDownloads(true)
@@ -555,7 +559,7 @@ export default function RootLayout ({
<div key={i} className='popup-entry'>
<p className='text-2xl'>{v.name}</p>
<div className='flex gap-2'>
<div className='entry-info-item'>
<div className='entry-info-item btntheme3'>
<p>
{(() => {
const data = getVersionsAmountData(v.id)
@@ -566,7 +570,7 @@ export default function RootLayout ({
</p>
</div>
<div
className='entry-info-item'
className='entry-info-item btntheme3'
hidden={!v.official}
>
<FontAwesomeIcon
@@ -576,7 +580,7 @@ export default function RootLayout ({
<p>Official</p>
</div>
<div
className='entry-info-item'
className='entry-info-item btntheme3'
hidden={v.official}
>
<FontAwesomeIcon
@@ -591,7 +595,7 @@ export default function RootLayout ({
</div>
</div>
<div
className='entry-info-item mt-2'
className='entry-info-item btntheme3 mt-2'
hidden={v.official}
>
<FontAwesomeIcon
@@ -601,7 +605,7 @@ export default function RootLayout ({
<p>Developer: {v.developer}</p>
</div>
<button
className='button right-2 bottom-2'
className='button btntheme3 right-2 bottom-2'
onClick={() => setSelectedGame(v.id)}
>
<>
@@ -635,7 +639,7 @@ export default function RootLayout ({
}{' '}
v{getVersionInfo(v.version)?.versionName}
</p>
<div className='mt-[25px] flex items-center justify-between'>
<div className='mt-6.25 flex items-center justify-between'>
{v.failed ? (
<>
<div className='flex items-center'>
@@ -643,7 +647,7 @@ export default function RootLayout ({
Download failed
</span>
<button
className='button ml-30 mb-2'
className='button btntheme3 ml-30 mb-2'
onClick={() => {
setDownloadProgress(prev =>
prev.filter(
@@ -723,7 +727,8 @@ export default function RootLayout ({
</p>
<div className='popup-content flex flex-col items-center justify-center gap-2 h-full'>
<button
className='button'
className='button btntheme2'
disabled={downloadProgress.length != 0}
onClick={() =>
invoke('uninstall_version', {
name: managingVersion
@@ -733,7 +738,7 @@ export default function RootLayout ({
Uninstall
</button>
<button
className='button'
className='button btntheme2'
onClick={async () =>
invoke('open_folder', {
name: managingVersion
@@ -763,7 +768,7 @@ export default function RootLayout ({
serverVersionList != null && (
<div className='flex justify-center'>
<button
className='button w-fit mt-2 -mb-4'
className='button btntheme1 w-fit mt-2 -mb-4'
onClick={() => {
setFadeOut(true)
setTimeout(() => setShowPopup(false), 200)
@@ -774,7 +779,7 @@ export default function RootLayout ({
{selectedVersionList.length == 1 ? '' : 's'}
</button>
<button
className='button w-fit mt-2 ml-2 -mb-4'
className='button btntheme1 w-fit mt-2 ml-2 -mb-4'
onClick={() => {
const allIds = getSpecialVersionsList(
selectedGame

View File

@@ -1,6 +1,6 @@
'use client'
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import './Installs.css'
import { useGlobal } from './GlobalProvider'
import Link from 'next/link'
@@ -11,6 +11,7 @@ import {
faWarning
} from '@fortawesome/free-solid-svg-icons'
import { platform } from '@tauri-apps/plugin-os'
import { useRouter } from 'next/navigation'
export default function Installs () {
const {
@@ -27,6 +28,9 @@ export default function Installs () {
getVersionsAmountData
} = useGlobal()
const router = useRouter()
const [hoveredIds, setHoveredIds] = useState<number[]>([])
useEffect(() => {
if (!showPopup) return
setSelectedVersionList([])
@@ -37,7 +41,7 @@ export default function Installs () {
<div className='flex justify-between items-center mb-4'>
<p className='text-3xl'>Games</p>
<button
className='button text-3xl'
className='button btntheme1 text-3xl'
onClick={() => {
setSelectedGame(null)
setPopupMode(0)
@@ -63,11 +67,31 @@ export default function Installs () {
return a.id - b.id
})
.map(i => (
<div key={i.id} className='downloads-entry'>
<div
key={i.id}
className={`downloads-entry ${
normalConfig?.settings.useLegacyInteractButtons
? ''
: 'cursor-pointer'
}`}
title='Click to view game installs'
onClick={() => {
if (normalConfig?.settings.useLegacyInteractButtons) return
router.push('/game?id=' + i.id)
}}
onMouseEnter={() => setHoveredIds(prev => [...prev, i.id])}
onMouseLeave={() =>
setHoveredIds(prev => prev.filter(e => e !== i.id))
}
>
<div className='flex flex-col'>
<p className='text-2xl'>{i.name}</p>
<div className='flex gap-2'>
<div className='entry-info-item'>
<div
className={`entry-info-item ${
hoveredIds.includes(i.id) ? 'btntheme3' : 'btntheme2'
}`}
>
<p>
{(() => {
const data = getVersionsAmountData(i.id)
@@ -77,11 +101,21 @@ export default function Installs () {
versions installed
</p>
</div>
<div className='entry-info-item' hidden={!i.official}>
<div
className={`entry-info-item ${
hoveredIds.includes(i.id) ? 'btntheme3' : 'btntheme2'
}`}
hidden={!i.official}
>
<FontAwesomeIcon icon={faCheck} color='#19c84b' />
<p>Official</p>
</div>
<div className='entry-info-item' hidden={i.official}>
<div
className={`entry-info-item ${
hoveredIds.includes(i.id) ? 'btntheme3' : 'btntheme2'
}`}
hidden={i.official}
>
<FontAwesomeIcon
icon={i.verified ? faShieldHalved : faWarning}
color={i.verified ? '#19c84b' : '#ffc800'}
@@ -90,8 +124,16 @@ export default function Installs () {
</div>
</div>
</div>
<div className='flex flex-row items-center gap-2'>
<Link className='button' href={'/game?id=' + i.id}>
<div
className='flex flex-row items-center gap-2'
hidden={!normalConfig?.settings.useLegacyInteractButtons}
>
<Link
className={`button ${
hoveredIds.includes(i.id) ? 'btntheme3' : 'btntheme2'
}`}
href={'/game?id=' + i.id}
>
Installs
</Link>
</div>

View File

@@ -10,6 +10,8 @@ export default function Settings () {
const [allowNotifications, setAllowNotifications] = useState(false)
const [alwaysShowGamesInSidebar, setAlwaysShowGamesInSidebar] =
useState(false)
const [useLegacyInteractButtons, setUseLegacyInteractButtons] =
useState(false)
const [theme, setTheme] = useState(0)
const [loaded, setLoaded] = useState(false)
@@ -22,6 +24,9 @@ export default function Settings () {
setAlwaysShowGamesInSidebar(
normalConfig.settings.alwaysShowGamesInSidebar
)
setUseLegacyInteractButtons(
normalConfig.settings.useLegacyInteractButtons
)
setTheme(normalConfig.settings.theme)
setLoaded(true)
break
@@ -82,6 +87,30 @@ export default function Settings () {
}
}}
/>
<Setting
label='Show Installs/Launch Buttons'
value={useLegacyInteractButtons}
onChange={async () => {
while (normalConfig != null) {
setUseLegacyInteractButtons(!useLegacyInteractButtons)
setNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
useLegacyInteractButtons: !useLegacyInteractButtons
}
})
writeNormalConfig({
...normalConfig,
settings: {
...normalConfig.settings,
useLegacyInteractButtons: !useLegacyInteractButtons
}
})
break
}
}}
/>
<div>
<label className='text-lg'>Theme:</label>
<select

View File

@@ -2,6 +2,7 @@ export class SettingsType {
constructor (
public allowNotifications: boolean = true,
public alwaysShowGamesInSidebar: boolean = true,
public useLegacyInteractButtons: boolean = false,
public theme: number = 0
) {}
}