Navbar wireup (#98)

* Navbar wireup

* Fix height issue

* Fix syncing

* working branch

* Added root directories to breadcrumbs

* fix jre detect

---------

Co-authored-by: Jai A <jaiagr+gpg@pm.me>
This commit is contained in:
Adrian O.V
2023-04-26 23:19:37 -04:00
committed by GitHub
parent f0b8a708a3
commit bda63d5d64
29 changed files with 631 additions and 299 deletions

View File

@@ -1,20 +1,14 @@
<script setup>
import { ref, onMounted } from 'vue'
import { RouterView, RouterLink } from 'vue-router'
import {
ChevronLeftIcon,
ChevronRightIcon,
HomeIcon,
SearchIcon,
LibraryIcon,
PlusIcon,
SettingsIcon,
} from 'omorphia'
import { HomeIcon, SearchIcon, LibraryIcon, PlusIcon, SettingsIcon } from 'omorphia'
import { useTheming } from '@/store/state'
import AccountsCard from '@/components/ui/AccountsCard.vue'
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
import { list } from '@/helpers/profile'
import { get } from '@/helpers/settings'
import Breadcrumbs from '@/components/ui/Breadcrumbs.vue'
import RunningAppBar from '@/components/ui/RunningAppBar.vue'
const themeStore = useTheming()
@@ -65,12 +59,12 @@ list().then(
<div class="view">
<div class="appbar">
<section class="navigation-controls">
<ChevronLeftIcon @click="$router.back()" />
<ChevronRightIcon @click="$router.forward()" />
<p>{{ $route.name }}</p>
<Breadcrumbs />
</section>
<section class="mod-stats">
<p>{{ installedMods }} mods installed</p>
<Suspense>
<RunningAppBar />
</Suspense>
</section>
</div>
<div class="router-view">
@@ -90,7 +84,7 @@ list().then(
overflow: hidden;
.view {
width: 100%;
width: calc(100% - 5rem);
.appbar {
display: flex;
@@ -98,7 +92,8 @@ list().then(
align-items: center;
background: var(--color-super-raised-bg);
text-align: center;
padding: 0.5rem 1rem;
padding: 0 0 0 1rem;
height: 3.25rem;
z-index: 11;
.navigation-controls {
@@ -132,6 +127,7 @@ list().then(
}
.mod-stats {
height: 100%;
display: inherit;
align-items: inherit;
justify-content: flex-end;
@@ -140,7 +136,7 @@ list().then(
.router-view {
width: 100%;
height: calc(100% - 2rem);
height: calc(100% - 3.125rem);
overflow: auto;
overflow-x: hidden;
}

View File

@@ -2,3 +2,5 @@ export { default as PlayIcon } from './play.svg'
export { default as OpenFolderIcon } from './folder-open.svg'
export { default as BrowseIcon } from './folder-search.svg'
export { default as LoginIcon } from './log-in.svg'
export { default as StopIcon } from './stop-circle.svg'
export { default as TerminalIcon } from './terminal-square.svg'

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-stop-circle"><circle cx="12" cy="12" r="10"></circle><rect width="6" height="6" x="9" y="9"></rect></svg>

After

Width:  |  Height:  |  Size: 307 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-terminal-square"><path d="m7 11 2-2-2-2"></path><path d="M11 13h4"></path><rect width="18" height="18" x="3" y="3" rx="2" ry="2"></rect></svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -0,0 +1,64 @@
<template>
<div class="breadcrumbs">
<div v-for="breadcrumb in breadcrumbs" :key="breadcrumb.name" class="breadcrumbs__item">
<router-link
v-if="breadcrumb.link"
:to="breadcrumb.link.replace('{id}', encodeURIComponent($route.params.id))"
>{{
breadcrumb.name.charAt(0) === '?'
? breadcrumbData.getName(breadcrumb.name.slice(1))
: breadcrumb.name
}}
</router-link>
<span v-else class="selected">{{
breadcrumb.name.charAt(0) === '?'
? breadcrumbData.getName(breadcrumb.name.slice(1))
: breadcrumb.name
}}</span>
<ChevronRightIcon v-if="breadcrumb.link" class="chevron" />
</div>
</div>
</template>
<script setup>
import { ChevronRightIcon } from 'omorphia'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { useRoute } from 'vue-router'
import { computed } from 'vue'
const route = useRoute()
const breadcrumbData = useBreadcrumbs()
const breadcrumbs = computed(() => {
const additionalContext =
route.meta.useContext === true
? breadcrumbData.context
: route.meta.useRootContext === true
? breadcrumbData.rootContext
: null
return additionalContext ? [additionalContext, ...route.meta.breadcrumb] : route.meta.breadcrumb
})
</script>
<style scoped lang="scss">
.breadcrumbs {
display: flex;
flex-direction: row;
.breadcrumbs__item {
display: flex;
flex-direction: row;
vertical-align: center;
margin: auto 0;
.chevron,
a {
margin: auto 0;
}
}
}
.selected {
color: var(--color-contrast);
}
</style>

View File

@@ -0,0 +1,103 @@
<template>
<div v-if="currentProcesses[0]" class="status">
<span class="circle running" />
<span class="running-text">
{{ currentProcesses[0].metadata.name }}
</span>
<Button icon-only class="icon-button stop" @click="stop()">
<StopIcon />
</Button>
<Button icon-only class="icon-button" @click="goToTerminal()">
<TerminalIcon />
</Button>
</div>
<div v-else class="status">
<span class="circle stopped" />
<span class="running-text"> No running profiles </span>
</div>
</template>
<script setup>
import { Button } from 'omorphia'
import { StopIcon, TerminalIcon } from '@/assets/icons'
import { ref } from 'vue'
import {
get_all_running_profiles as getRunningProfiles,
kill_by_uuid as killProfile,
get_uuids_by_profile_path as getProfileProcesses,
} from '@/helpers/process'
import { process_listener } from '@/helpers/events'
import { useRouter } from 'vue-router'
const router = useRouter()
const currentProcesses = ref(await getRunningProfiles())
await process_listener(async () => {
await refresh()
})
const refresh = async () => {
currentProcesses.value = await getRunningProfiles()
}
const stop = async () => {
try {
const processes = await getProfileProcesses(currentProcesses.value[0].path)
await killProfile(processes[0])
} catch (e) {
console.error(e)
}
await refresh()
}
const goToTerminal = () => {
router.push(`/instance/${encodeURIComponent(currentProcesses.value[0].path)}/logs`)
}
</script>
<style scoped lang="scss">
.status {
height: 100%;
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
background-color: var(--color-raised-bg);
padding: 0 1rem;
margin: 0;
}
.running-text {
white-space: nowrap;
overflow: hidden;
}
.circle {
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
display: inline-block;
margin-right: 0.25rem;
&.running {
background-color: var(--color-brand);
}
&.stopped {
background-color: var(--color-base);
}
}
.icon-button {
background-color: rgba(0, 0, 0, 0);
box-shadow: none;
width: 1.25rem !important;
height: 1.25rem !important;
&.stop {
--text-color: var(--color-red) !important;
}
}
</style>

View File

@@ -31,8 +31,8 @@ export async function get_all_running_uuids() {
/// Gets all running process IDs with a given profile path
/// Returns [u32]
export async function get_uuids_by_profile_path(profile_path) {
return await invoke('process_get_uuids_by_profile_path', { profile_path })
export async function get_uuids_by_profile_path(profilePath) {
return await invoke('process_get_uuids_by_profile_path', { profilePath })
}
/// Gets all running process IDs with a given profile path
@@ -43,8 +43,8 @@ export async function get_all_running_profile_paths(profile_path) {
/// Gets all running process IDs with a given profile path
/// Returns [u32]
export async function get_all_running_profiles(profile_path) {
return await invoke('process_get_all_running_profiles', { profile_path })
export async function get_all_running_profiles() {
return await invoke('process_get_all_running_profiles')
}
/// Gets process stderr by UUID

View File

@@ -17,9 +17,14 @@ import {
} from 'omorphia'
import Multiselect from 'vue-multiselect'
import { useSearch } from '@/store/state'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import { get_categories, get_loaders, get_game_versions } from '@/helpers/tags'
import { useRoute } from 'vue-router'
const searchStore = useSearch()
const breadcrumbs = useBreadcrumbs()
const route = useRoute()
breadcrumbs.setContext({ name: 'Browse', link: route.path })
const showSnapshots = ref(false)
const loading = ref(true)

View File

@@ -2,9 +2,16 @@
import RowDisplay from '@/components/RowDisplay.vue'
import { shallowRef } from 'vue'
import { list } from '@/helpers/profile.js'
import { useRoute } from 'vue-router'
import { useBreadcrumbs } from '@/store/breadcrumbs'
const route = useRoute()
const breadcrumbs = useBreadcrumbs()
const profiles = await list()
const recentInstances = shallowRef(Object.values(profiles))
breadcrumbs.setRootContext({ name: 'Home', link: route.path })
</script>
<template>

View File

@@ -2,9 +2,16 @@
import GridDisplay from '@/components/GridDisplay.vue'
import { shallowRef } from 'vue'
import { list } from '@/helpers/profile.js'
import { useRoute } from 'vue-router'
import { useBreadcrumbs } from '@/store/breadcrumbs'
const route = useRoute()
const breadcrumbs = useBreadcrumbs()
const profiles = await list()
const instances = shallowRef(Object.values(profiles))
breadcrumbs.setRootContext({ name: 'Library', link: route.path })
</script>
<template>

View File

@@ -5,9 +5,9 @@
<Avatar size="lg" :src="convertFileSrc(instance.metadata.icon)" />
<div class="instance-info">
<h2 class="name">{{ instance.metadata.name }}</h2>
<span class="metadata"
>{{ instance.metadata.loader }} {{ instance.metadata.game_version }}</span
>
<span class="metadata">
{{ instance.metadata.loader }} {{ instance.metadata.game_version }}
</span>
</div>
<span class="button-group">
<Button color="primary" class="instance-button" @click="run($route.params.id)">
@@ -47,9 +47,17 @@ import { get, run } from '@/helpers/profile'
import { useRoute } from 'vue-router'
import { shallowRef } from 'vue'
import { convertFileSrc } from '@tauri-apps/api/tauri'
import { useBreadcrumbs } from '@/store/breadcrumbs'
const breadcrumbs = useBreadcrumbs()
const route = useRoute()
const instance = shallowRef(await get(route.params.id))
breadcrumbs.setName('Instance', instance.value.metadata.name)
breadcrumbs.setContext({
name: instance.value.metadata.name,
link: route.path,
})
</script>
<style scoped lang="scss">
@@ -77,7 +85,7 @@ Button {
flex-direction: column;
padding: 1rem;
background: var(--color-raised-bg);
min-height: calc(100% - 2rem);
min-height: calc(100% - 3.25rem);
overflow: hidden;
}

View File

@@ -36,7 +36,7 @@
</div>
<div v-for="mod in search" :key="mod.file_name" class="table-row">
<div class="table-cell table-text">
<Button v-if="true" icon-only>
<Button v-if="mod.outdated" icon-only>
<UpdatedIcon />
</Button>
<Button v-else disabled icon-only>
@@ -106,6 +106,7 @@ for (const project of Object.values(props.instance.projects)) {
file_name: project.file_name,
icon: project.metadata.project.icon_url,
disabled: project.disabled,
outdated: project.metadata.update_version,
})
} else if (project.metadata.type === 'inferred') {
projects.value.push({
@@ -115,6 +116,7 @@ for (const project of Object.values(props.instance.projects)) {
file_name: project.file_name,
icon: project.metadata.icon ? convertFileSrc(project.metadata.icon) : null,
disabled: project.disabled,
outdated: false,
})
} else {
projects.value.push({
@@ -124,6 +126,7 @@ for (const project of Object.values(props.instance.projects)) {
file_name: project.file_name,
icon: null,
disabled: project.disabled,
outdated: false,
})
}
}

View File

@@ -218,9 +218,11 @@ import { ofetch } from 'ofetch'
import { useRoute, useRouter } from 'vue-router'
import { ref, shallowRef, watch } from 'vue'
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
import { useBreadcrumbs } from '@/store/breadcrumbs'
const route = useRoute()
const router = useRouter()
const breadcrumbs = useBreadcrumbs()
const confirmModal = ref(null)
const loaders = ref(await get_loaders())
@@ -232,6 +234,8 @@ const [data, versions, members, dependencies] = await Promise.all([
ofetch(`https://api.modrinth.com/v2/project/${route.params.id}/dependencies`).then(shallowRef),
])
breadcrumbs.setName('Project', data.value.title)
watch(
() => route.params.id,
() => {
@@ -246,7 +250,9 @@ async function install(version) {
const packs = Object.values(await list())
if (
packs.length === 0 ||
!packs.map((value) => value.metadata).find((pack) => pack.linked_project_id === data.value.id)
!packs
.map((value) => value.metadata)
.find((pack) => pack.linked_data?.project_id === data.value.id)
) {
let id = await pack_install(version)
await router.push({ path: `/instance/${encodeURIComponent(id)}` })

View File

@@ -183,6 +183,9 @@ import {
import { releaseColor } from '@/helpers/utils'
import { ref, defineProps } from 'vue'
import { useRoute } from 'vue-router'
import { useBreadcrumbs } from '@/store/breadcrumbs'
const breadcrumbs = useBreadcrumbs()
const route = useRoute()
@@ -210,7 +213,10 @@ const props = defineProps({
})
const version = ref(props.versions.find((version) => version.id === route.params.version))
breadcrumbs.setName('Version', version.value.name)
const author = ref(props.members.find((member) => member.user.id === version.value.author_id))
const displayDependencies = ref(
version.value.dependencies.map((dependency) => {
const version = props.dependencies.versions.find((obj) => obj.id === dependency.version_id)

View File

@@ -13,31 +13,33 @@ export default new createRouter({
path: '/',
name: 'Home',
component: Pages.Index,
meta: {
breadcrumb: [{ name: 'Home' }],
},
},
{
path: '/browse',
name: 'Browse',
component: Pages.Browse,
meta: {
breadcrumb: [{ name: 'Browse' }],
},
},
{
path: '/library',
name: 'Library',
component: Pages.Library,
},
{
path: '/add-instance',
name: 'Add Instance',
component: Pages.AddInstance,
},
{
path: '/project',
name: 'Project',
component: Pages.Project,
meta: {
breadcrumb: [{ name: 'Library' }],
},
},
{
path: '/settings',
name: 'Settings',
component: Pages.Settings,
meta: {
breadcrumb: [{ name: 'Settings' }],
},
},
{
path: '/project/:id',
@@ -49,22 +51,42 @@ export default new createRouter({
path: '',
name: 'Description',
component: Project.Description,
meta: {
useContext: true,
breadcrumb: [{ name: '?Project' }],
},
},
{
path: 'versions',
name: 'Versions',
component: Project.Versions,
meta: {
useContext: true,
breadcrumb: [{ name: '?Project', link: '/project/{id}/' }, { name: 'Versions' }],
},
},
{
path: 'version/:version',
name: 'Version',
component: Project.Version,
props: true,
meta: {
useContext: true,
breadcrumb: [
{ name: '?Project', link: '/project/{id}/' },
{ name: 'Versions', link: '/project/{id}/versions' },
{ name: '?Version' },
],
},
},
{
path: 'gallery',
name: 'Gallery',
component: Project.Gallery,
meta: {
useContext: true,
breadcrumb: [{ name: '?Project', link: '/project/{id}/' }, { name: 'Gallery' }],
},
},
],
},
@@ -78,16 +100,28 @@ export default new createRouter({
path: '',
name: 'Mods',
component: Instance.Mods,
meta: {
useRootContext: true,
breadcrumb: [{ name: '?Instance' }],
},
},
{
path: 'options',
name: 'Options',
component: Instance.Options,
meta: {
useRootContext: true,
breadcrumb: [{ name: '?Instance', link: '/instance/{id}/' }, { name: 'Options' }],
},
},
{
path: 'logs',
name: 'Logs',
component: Instance.Logs,
meta: {
useRootContext: true,
breadcrumb: [{ name: '?Instance', link: '/instance/{id}/' }, { name: 'Logs' }],
},
},
],
},

View File

@@ -0,0 +1,23 @@
import { defineStore } from 'pinia'
export const useBreadcrumbs = defineStore('breadcrumbsStore', {
state: () => ({
names: new Map(),
context: null,
rootContext: null,
}),
actions: {
getName(route) {
return this.names.get(route) ?? route
},
setName(route, title) {
this.names.set(route, title)
},
setContext(context) {
this.context = context
},
setRootContext(context) {
this.rootContext = context
},
},
})

View File

@@ -1,4 +1,5 @@
import { useSearch } from './search'
import { useTheming } from './theme'
import { useBreadcrumbs } from './breadcrumbs'
export { useSearch, useTheming }
export { useSearch, useTheming, useBreadcrumbs }