forked from akanyan/STARTLINER
feat: new error banner, qol
This commit is contained in:
@ -63,7 +63,7 @@ pub async fn startline(app: AppHandle, refresh: bool) -> Result<(), String> {
|
|||||||
if let Some(p) = &mut appd.profile {
|
if let Some(p) = &mut appd.profile {
|
||||||
log::debug!("{}", hash);
|
log::debug!("{}", hash);
|
||||||
p.line_up(hash, refresh, app.clone()).await
|
p.line_up(hash, refresh, app.clone()).await
|
||||||
.map_err(|e| format!("Lineup failed:\n{}", e))?;
|
.map_err(|e| e.to_string())?;
|
||||||
let app_clone = app.clone();
|
let app_clone = app.clone();
|
||||||
let p_clone = p.clone();
|
let p_clone = p.clone();
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
|
@ -88,7 +88,9 @@ impl Display {
|
|||||||
settings.borrow_mut().resolution = Resolution::new(width, height);
|
settings.borrow_mut().resolution = Resolution::new(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
display_set.apply()?;
|
display_set.apply().map_err(
|
||||||
|
|_| anyhow!("The selected monitor has been disconnected or doesn't support the chosen display mode")
|
||||||
|
)?;
|
||||||
displayz::refresh()?;
|
displayz::refresh()?;
|
||||||
|
|
||||||
log::debug!("prepare display: done");
|
log::debug!("prepare display: done");
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Ref, computed, onMounted, ref } from 'vue';
|
import { Ref, computed, onMounted, ref } from 'vue';
|
||||||
import Button from 'primevue/button';
|
import Button from 'primevue/button';
|
||||||
|
import Dialog from 'primevue/dialog';
|
||||||
import InputIcon from 'primevue/inputicon';
|
import InputIcon from 'primevue/inputicon';
|
||||||
import InputText from 'primevue/inputtext';
|
import InputText from 'primevue/inputtext';
|
||||||
import Tab from 'primevue/tab';
|
import Tab from 'primevue/tab';
|
||||||
@ -8,13 +9,19 @@ import TabList from 'primevue/tablist';
|
|||||||
import TabPanel from 'primevue/tabpanel';
|
import TabPanel from 'primevue/tabpanel';
|
||||||
import TabPanels from 'primevue/tabpanels';
|
import TabPanels from 'primevue/tabpanels';
|
||||||
import Tabs from 'primevue/tabs';
|
import Tabs from 'primevue/tabs';
|
||||||
|
import { listen } from '@tauri-apps/api/event';
|
||||||
import ModList from './ModList.vue';
|
import ModList from './ModList.vue';
|
||||||
import ModStore from './ModStore.vue';
|
import ModStore from './ModStore.vue';
|
||||||
import OptionList from './OptionList.vue';
|
import OptionList from './OptionList.vue';
|
||||||
import ProfileList from './ProfileList.vue';
|
import ProfileList from './ProfileList.vue';
|
||||||
import StartButton from './StartButton.vue';
|
import StartButton from './StartButton.vue';
|
||||||
import { invoke } from '../invoke';
|
import { invoke } from '../invoke';
|
||||||
import { useClientStore, useGeneralStore, usePkgStore, usePrfStore } from '../stores';
|
import {
|
||||||
|
useClientStore,
|
||||||
|
useGeneralStore,
|
||||||
|
usePkgStore,
|
||||||
|
usePrfStore,
|
||||||
|
} from '../stores';
|
||||||
import { Dirs } from '../types';
|
import { Dirs } from '../types';
|
||||||
|
|
||||||
const pkg = usePkgStore();
|
const pkg = usePkgStore();
|
||||||
@ -56,35 +63,81 @@ onMounted(async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const errorVisible = ref(false);
|
||||||
|
const errorMessage = ref('No error');
|
||||||
|
const errorHeader = ref('No header');
|
||||||
|
|
||||||
|
listen<{ message: string; header: string }>('invoke-error', (event) => {
|
||||||
|
errorVisible.value = true;
|
||||||
|
errorMessage.value = event.payload.message;
|
||||||
|
errorHeader.value = event.payload.header;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main :class="client.scaleFactor === 's' ? 'main-scale-s' : client.scaleFactor === 'm' ? 'main-scale-m' : client.scaleFactor === 'l' ? 'main-scale-l' : 'main-scale-xl'">
|
<main
|
||||||
|
:class="
|
||||||
|
client.scaleFactor === 's'
|
||||||
|
? 'main-scale-s'
|
||||||
|
: client.scaleFactor === 'm'
|
||||||
|
? 'main-scale-m'
|
||||||
|
: client.scaleFactor === 'l'
|
||||||
|
? 'main-scale-l'
|
||||||
|
: 'main-scale-xl'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<Dialog
|
||||||
|
modal
|
||||||
|
:visible="errorVisible"
|
||||||
|
:closable="false /*this shit doesn't work */"
|
||||||
|
:header="errorHeader"
|
||||||
|
:style="{ width: '50vw' }"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
{{ errorMessage }}
|
||||||
|
<Button
|
||||||
|
class="m-auto"
|
||||||
|
label="A sad state of affairs"
|
||||||
|
@click="errorVisible = false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
lazy
|
lazy
|
||||||
:value="currentTab"
|
:value="currentTab"
|
||||||
v-on:update:value="(value) => { currentTab = value; }"
|
v-on:update:value="
|
||||||
|
(value) => {
|
||||||
|
currentTab = value;
|
||||||
|
}
|
||||||
|
"
|
||||||
class="h-screen"
|
class="h-screen"
|
||||||
>
|
>
|
||||||
<div class="fixed w-full flex z-100">
|
<div class="fixed w-full flex z-100">
|
||||||
<TabList class="grow" :show-navigators="false">
|
<TabList class="grow" :show-navigators="false">
|
||||||
<Tab :value="3"
|
<Tab :value="3"
|
||||||
><div class="pi pi-users"></div
|
><div class="pi pi-users" v-tooltip="'Profiles'"></div
|
||||||
></Tab>
|
></Tab>
|
||||||
<Tab :disabled="isProfileDisabled" :value="0"
|
<Tab :disabled="isProfileDisabled" :value="0"
|
||||||
><div class="pi pi-box"></div
|
><div
|
||||||
|
class="pi pi-box"
|
||||||
|
v-tooltip="'Installed packages'"
|
||||||
|
></div
|
||||||
></Tab>
|
></Tab>
|
||||||
<Tab v-if="prf.current?.meta.game === 'chunithm'" :value="4"
|
<Tab v-if="prf.current?.meta.game === 'chunithm'" :value="4"
|
||||||
><div class="pi pi-ticket"></div
|
><div class="pi pi-ticket" v-tooltip="'Patches'"></div
|
||||||
></Tab>
|
></Tab>
|
||||||
<Tab
|
<Tab
|
||||||
v-if="pkg.networkStatus === 'online'"
|
v-if="pkg.networkStatus === 'online'"
|
||||||
:disabled="isProfileDisabled"
|
:disabled="isProfileDisabled"
|
||||||
:value="1"
|
:value="1"
|
||||||
><div class="pi pi-download"></div
|
><div
|
||||||
|
class="pi pi-download"
|
||||||
|
v-tooltip="'Package store'"
|
||||||
|
></div
|
||||||
></Tab>
|
></Tab>
|
||||||
<Tab :disabled="isProfileDisabled" :value="2"
|
<Tab :disabled="isProfileDisabled" :value="2"
|
||||||
><div class="pi pi-cog"></div
|
><div class="pi pi-cog" v-tooltip="'Settings'"></div
|
||||||
></Tab>
|
></Tab>
|
||||||
|
|
||||||
<div class="grow"></div>
|
<div class="grow"></div>
|
||||||
@ -96,7 +149,7 @@ onMounted(async () => {
|
|||||||
</InputIcon>
|
</InputIcon>
|
||||||
<InputText
|
<InputText
|
||||||
v-if="currentTab === 2"
|
v-if="currentTab === 2"
|
||||||
style="min-width: 0; width: 25dvw;"
|
style="min-width: 0; width: 25dvw"
|
||||||
class="self-center"
|
class="self-center"
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
@ -104,7 +157,7 @@ onMounted(async () => {
|
|||||||
/>
|
/>
|
||||||
<InputText
|
<InputText
|
||||||
v-else
|
v-else
|
||||||
style="min-width: 0; width: 25dvw;"
|
style="min-width: 0; width: 25dvw"
|
||||||
class="self-center"
|
class="self-center"
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
@ -119,14 +172,20 @@ onMounted(async () => {
|
|||||||
:disabled="true"
|
:disabled="true"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
v-if="pkg.networkStatus === 'offline' && !client.offlineMode"
|
v-if="
|
||||||
|
pkg.networkStatus === 'offline' &&
|
||||||
|
!client.offlineMode
|
||||||
|
"
|
||||||
class="shrink self-center"
|
class="shrink self-center"
|
||||||
icon="pi pi-sync"
|
icon="pi pi-sync"
|
||||||
size="small"
|
size="small"
|
||||||
@click="pkg.fetch(false)"
|
@click="pkg.fetch(false)"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
v-if="pkg.networkStatus === 'online' && pkg.hasAvailableUpdates"
|
v-if="
|
||||||
|
pkg.networkStatus === 'online' &&
|
||||||
|
pkg.hasAvailableUpdates
|
||||||
|
"
|
||||||
icon="pi pi-download"
|
icon="pi pi-download"
|
||||||
label="UPDATE ALL"
|
label="UPDATE ALL"
|
||||||
size="small"
|
size="small"
|
||||||
@ -161,8 +220,13 @@ onMounted(async () => {
|
|||||||
</footer>
|
</footer>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel :value="4">
|
<TabPanel :value="4">
|
||||||
CHUNITHM patches are not yet implemented.<br />Use
|
CHUNITHM patches are not implemented yet.<br />Use
|
||||||
<a href=https://patcher.two-torial.xyz/ target="_blank" style="text-decoration: underline;">patcher.two-torial.xyz</a>
|
<a
|
||||||
|
href="https://patcher.two-torial.xyz/"
|
||||||
|
target="_blank"
|
||||||
|
style="text-decoration: underline"
|
||||||
|
>patcher.two-torial.xyz</a
|
||||||
|
>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
<div v-if="currentTab === 5 || currentTab === 3">
|
<div v-if="currentTab === 5 || currentTab === 3">
|
||||||
@ -196,19 +260,19 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.main-scale-s {
|
.main-scale-s {
|
||||||
zoom: 1.0
|
zoom: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-scale-m {
|
.main-scale-m {
|
||||||
zoom: 1.25
|
zoom: 1.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-scale-l {
|
.main-scale-l {
|
||||||
zoom: 1.4
|
zoom: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-scale-xl {
|
.main-scale-xl {
|
||||||
zoom: 1.7
|
zoom: 1.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-tablist {
|
.p-tablist {
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Button from 'primevue/button';
|
import Button from 'primevue/button';
|
||||||
import InputText from 'primevue/inputtext';
|
import InputText from 'primevue/inputtext';
|
||||||
|
import * as path from '@tauri-apps/api/path';
|
||||||
import { open } from '@tauri-apps/plugin-dialog';
|
import { open } from '@tauri-apps/plugin-dialog';
|
||||||
|
import { usePrfStore } from '../stores';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
placeholder: String,
|
placeholder: String,
|
||||||
@ -12,10 +14,25 @@ const props = defineProps({
|
|||||||
callback: Function,
|
callback: Function,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const prf = usePrfStore();
|
||||||
|
|
||||||
const filePick = async () => {
|
const filePick = async () => {
|
||||||
|
const exePath = prf.current?.data.sgt.target;
|
||||||
|
let defaultPath: string | undefined;
|
||||||
|
if (
|
||||||
|
exePath !== undefined &&
|
||||||
|
exePath.length > 0 &&
|
||||||
|
props.value !== undefined &&
|
||||||
|
!(await path.isAbsolute(props.value))
|
||||||
|
) {
|
||||||
|
defaultPath = await path.join(exePath, '..');
|
||||||
|
defaultPath = await path.join(defaultPath, props.value);
|
||||||
|
defaultPath = await path.join(defaultPath, '..');
|
||||||
|
}
|
||||||
const res = await open({
|
const res = await open({
|
||||||
multiple: false,
|
multiple: false,
|
||||||
directory: props.directory,
|
directory: props.directory,
|
||||||
|
defaultPath,
|
||||||
filters:
|
filters:
|
||||||
props.promptname && props.extension
|
props.promptname && props.extension
|
||||||
? [
|
? [
|
||||||
@ -28,7 +45,7 @@ const filePick = async () => {
|
|||||||
});
|
});
|
||||||
if (res != null && props.callback !== undefined) {
|
if (res != null && props.callback !== undefined) {
|
||||||
props.callback(res);
|
props.callback(res);
|
||||||
/*path.relative(cfgs.current?.data.exe_dir ?? '', res) */
|
/*path.relative(prf.current?.data.sgt.target ?? '', res) */
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -7,6 +7,9 @@ const prf = usePrfStore();
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div v-if="prf.list.length === 0">
|
||||||
|
Welcome to STARTLINER! Start by creating a profile.
|
||||||
|
</div>
|
||||||
<div class="mt-4 flex flex-row flex-wrap align-middle gap-4">
|
<div class="mt-4 flex flex-row flex-wrap align-middle gap-4">
|
||||||
<Button
|
<Button
|
||||||
label="O.N.G.E.K.I. profile"
|
label="O.N.G.E.K.I. profile"
|
||||||
|
@ -45,7 +45,7 @@ const updatesModel = computed({
|
|||||||
</OptionRow>
|
</OptionRow>
|
||||||
<OptionRow
|
<OptionRow
|
||||||
title="Offline mode"
|
title="Offline mode"
|
||||||
tooltip="Disables the package store. Applies after a restart"
|
tooltip="Disables the package store. Requires a restart."
|
||||||
>
|
>
|
||||||
<ToggleSwitch v-model="offlineModel" />
|
<ToggleSwitch v-model="offlineModel" />
|
||||||
</OptionRow>
|
</OptionRow>
|
||||||
|
@ -3,7 +3,7 @@ import {
|
|||||||
InvokeOptions,
|
InvokeOptions,
|
||||||
invoke as real_invoke,
|
invoke as real_invoke,
|
||||||
} from '@tauri-apps/api/core';
|
} from '@tauri-apps/api/core';
|
||||||
import { message } from '@tauri-apps/plugin-dialog';
|
import { emit } from '@tauri-apps/api/event';
|
||||||
|
|
||||||
export const invoke = async <T>(
|
export const invoke = async <T>(
|
||||||
cmd: string,
|
cmd: string,
|
||||||
@ -14,9 +14,9 @@ export const invoke = async <T>(
|
|||||||
return await real_invoke(cmd, args, options);
|
return await real_invoke(cmd, args, options);
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
if (typeof e === 'string') {
|
if (typeof e === 'string') {
|
||||||
await message(`${cmd}: ${e}`, {
|
emit('invoke-error', {
|
||||||
title: `Error`,
|
message: e,
|
||||||
kind: 'error',
|
header: `${cmd} failed`,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error(`Unresolved error: ${e}`);
|
console.error(`Unresolved error: ${e}`);
|
||||||
|
Reference in New Issue
Block a user