forked from akanyan/STARTLINER
feat: verbose toggle, info tab, many misc fixes
This commit is contained in:
@ -13,6 +13,7 @@ import TabPanel from 'primevue/tabpanel';
|
||||
import TabPanels from 'primevue/tabpanels';
|
||||
import Tabs from 'primevue/tabs';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import InfoPage from './InfoPage.vue';
|
||||
import ModList from './ModList.vue';
|
||||
import ModStore from './ModStore.vue';
|
||||
import OptionList from './OptionList.vue';
|
||||
@ -36,7 +37,8 @@ const client = useClientStore();
|
||||
|
||||
pkg.setupListeners();
|
||||
|
||||
const currentTab: Ref<string | number> = ref(3);
|
||||
const currentTab: Ref<'users' | 'loc' | 'patches' | 'rmt' | 'cfg' | 'info'> =
|
||||
ref('users');
|
||||
const pkgSearchTerm = ref('');
|
||||
|
||||
const isProfileDisabled = computed(() => prf.current === null);
|
||||
@ -62,20 +64,11 @@ onMounted(async () => {
|
||||
await Promise.all([prf.reloadList(), prf.reload()]);
|
||||
|
||||
if (prf.current !== null) {
|
||||
currentTab.value = 0;
|
||||
currentTab.value = 'loc';
|
||||
await pkg.reloadAll();
|
||||
}
|
||||
|
||||
fetch_promise.then(async () => {
|
||||
await invoke('install_package', {
|
||||
key: 'segatools-mu3hook',
|
||||
force: false,
|
||||
});
|
||||
await invoke('install_package', {
|
||||
key: 'segatools-chusanhook',
|
||||
force: false,
|
||||
});
|
||||
});
|
||||
await fetch_promise;
|
||||
});
|
||||
|
||||
const errorVisible = ref(false);
|
||||
@ -149,42 +142,47 @@ listen<{ message: string; header: string }>('invoke-error', (event) => {
|
||||
:value="currentTab"
|
||||
v-on:update:value="
|
||||
(value) => {
|
||||
currentTab = value;
|
||||
currentTab = value as any;
|
||||
}
|
||||
"
|
||||
class="h-screen"
|
||||
>
|
||||
<div class="fixed w-full flex z-100">
|
||||
<TabList class="grow" :show-navigators="false">
|
||||
<Tab :value="3"><div class="pi pi-users"></div></Tab>
|
||||
<Tab :disabled="isProfileDisabled" :value="0"
|
||||
<Tab value="users"><div class="pi pi-users"></div></Tab>
|
||||
<Tab :disabled="isProfileDisabled" value="loc"
|
||||
><div class="pi pi-box"></div
|
||||
></Tab>
|
||||
<Tab v-if="prf.current?.meta.game === 'chunithm'" :value="4"
|
||||
<Tab
|
||||
v-if="prf.current?.meta.game === 'chunithm'"
|
||||
value="patches"
|
||||
><div class="pi pi-ticket"></div
|
||||
></Tab>
|
||||
<Tab
|
||||
v-if="pkg.networkStatus === 'online'"
|
||||
:disabled="isProfileDisabled"
|
||||
:value="1"
|
||||
value="rmt"
|
||||
><div class="pi pi-download"></div
|
||||
></Tab>
|
||||
<Tab :disabled="isProfileDisabled" :value="2"
|
||||
<Tab :disabled="isProfileDisabled" value="cfg"
|
||||
><div class="pi pi-cog"></div
|
||||
></Tab>
|
||||
<Tab value="info"
|
||||
><div class="pi pi-info-circle"></div
|
||||
></Tab>
|
||||
|
||||
<div class="grow"></div>
|
||||
|
||||
<div class="flex gap-4">
|
||||
<div
|
||||
class="flex"
|
||||
v-if="[0, 1, 2].includes(currentTab as number)"
|
||||
v-if="['loc', 'rmt', 'cfg'].includes(currentTab)"
|
||||
>
|
||||
<InputIcon class="self-center mr-2">
|
||||
<i class="pi pi-search" />
|
||||
</InputIcon>
|
||||
<InputText
|
||||
v-if="currentTab === 2"
|
||||
v-if="currentTab === 'cfg'"
|
||||
style="min-width: 0; width: 25dvw"
|
||||
class="self-center"
|
||||
size="small"
|
||||
@ -234,28 +232,19 @@ listen<{ message: string; header: string }>('invoke-error', (event) => {
|
||||
</TabList>
|
||||
</div>
|
||||
<TabPanels class="w-full grow mt-[3rem]">
|
||||
<TabPanel :value="0">
|
||||
<TabPanel value="loc">
|
||||
<ModList :search="pkgSearchTerm" />
|
||||
</TabPanel>
|
||||
<TabPanel :value="1">
|
||||
<TabPanel value="rmt">
|
||||
<ModStore :search="pkgSearchTerm" />
|
||||
</TabPanel>
|
||||
<TabPanel :value="2">
|
||||
<TabPanel value="cfg">
|
||||
<OptionList />
|
||||
</TabPanel>
|
||||
<TabPanel :value="3">
|
||||
<TabPanel value="users">
|
||||
<ProfileList />
|
||||
<br /><br /><br />
|
||||
<footer>
|
||||
<Button
|
||||
icon="pi pi-discord"
|
||||
as="a"
|
||||
target="_blank"
|
||||
href="https://discord.gg/jxvzHjjEmc"
|
||||
/>
|
||||
</footer>
|
||||
</TabPanel>
|
||||
<TabPanel :value="4">
|
||||
<TabPanel value="patches">
|
||||
<PatchList
|
||||
v-if="
|
||||
pkg.hasLocal('mempatcher-mempatcher') &&
|
||||
@ -268,8 +257,11 @@ listen<{ message: string; header: string }>('invoke-error', (event) => {
|
||||
and enabled.
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel value="info">
|
||||
<InfoPage />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
<div v-if="currentTab === 5 || currentTab === 3">
|
||||
<div v-if="currentTab === 'users' || currentTab === 'info'">
|
||||
<img
|
||||
v-if="prf.current?.meta.game === 'ongeki'"
|
||||
src="/sticker-ongeki.svg"
|
||||
|
56
src/components/InfoPage.vue
Normal file
56
src/components/InfoPage.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import Button from 'primevue/button';
|
||||
import ScrollPanel from 'primevue/scrollpanel';
|
||||
import { invoke } from '../invoke';
|
||||
import { VueMarkdownIt } from '@f3ve/vue-markdown-it';
|
||||
|
||||
const changelog = ref('');
|
||||
|
||||
invoke('get_changelog').then((s) => (changelog.value = s as string));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>About</h1>
|
||||
STARTLINER is a simple launcher, configuration tool and mod manager for
|
||||
O.N.G.E.K.I. and CHUNITHM.
|
||||
<h1>Changelog</h1>
|
||||
<ScrollPanel style="height: 200px">
|
||||
<div class="changelog">
|
||||
<vue-markdown-it
|
||||
:source="changelog"
|
||||
:options="{ typographer: true, breaks: true }"
|
||||
/>
|
||||
</div>
|
||||
</ScrollPanel>
|
||||
<footer class="mt-10 flex gap-3">
|
||||
<Button
|
||||
icon="pi pi-discord"
|
||||
as="a"
|
||||
target="_blank"
|
||||
href="https://discord.gg/jxvzHjjEmc"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-github"
|
||||
as="a"
|
||||
target="_blank"
|
||||
href="https://gitea.tendokyu.moe/akanyan/STARTLINER"
|
||||
/>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style lang="css">
|
||||
h1 {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
|
||||
.changelog h2 {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
.changelog ul {
|
||||
list-style-type: circle;
|
||||
}
|
||||
.changelog li {
|
||||
margin-left: 40px;
|
||||
}
|
||||
</style>
|
@ -15,7 +15,8 @@ const props = defineProps({
|
||||
|
||||
const pkgs = usePkgStore();
|
||||
const prf = usePrfStore();
|
||||
const empty = ref(true);
|
||||
const groupCallIndex = ref(0);
|
||||
const empty = ref(false);
|
||||
const gameSublist: Ref<string[]> = ref([]);
|
||||
|
||||
invoke('get_game_packages', {
|
||||
@ -45,7 +46,10 @@ const group = computed(() => {
|
||||
({ namespace }) => namespace
|
||||
)
|
||||
);
|
||||
empty.value = Object.keys(res).length === 0;
|
||||
if (groupCallIndex.value > 0) {
|
||||
empty.value = Object.keys(res).length === 0;
|
||||
}
|
||||
groupCallIndex.value += 1;
|
||||
return res;
|
||||
});
|
||||
|
||||
@ -81,5 +85,5 @@ const missing = computed(() => {
|
||||
<Fieldset v-for="(namespace, key) in group" :legend="key.toString()">
|
||||
<ModListEntry v-for="p in namespace" :pkg="p" />
|
||||
</Fieldset>
|
||||
<div v-if="empty" class="text-3xl">∅</div>
|
||||
<div v-if="empty === true" class="text-3xl fadein">∅</div>
|
||||
</template>
|
||||
|
@ -1,10 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import Button from 'primevue/button';
|
||||
import ToggleSwitch from 'primevue/toggleswitch';
|
||||
import InstallButton from './InstallButton.vue';
|
||||
import LinkButton from './LinkButton.vue';
|
||||
import ModTitlecard from './ModTitlecard.vue';
|
||||
import UpdateButton from './UpdateButton.vue';
|
||||
import { invoke } from '../invoke';
|
||||
import { usePkgStore, usePrfStore } from '../stores';
|
||||
import { Feature, Package } from '../types';
|
||||
import { hasFeature } from '../util';
|
||||
@ -38,7 +40,7 @@ const model = computed({
|
||||
v-model="model"
|
||||
/>
|
||||
<InstallButton :pkg="pkg" />
|
||||
<!-- <Button
|
||||
<Button
|
||||
rounded
|
||||
icon="pi pi-folder"
|
||||
severity="help"
|
||||
@ -46,8 +48,10 @@ const model = computed({
|
||||
size="small"
|
||||
class="ml-2 shrink-0"
|
||||
style="width: 2rem; height: 2rem"
|
||||
v-on:click="pkg?.loc && open(pkg.loc.path ?? '')"
|
||||
/> -->
|
||||
v-on:click="
|
||||
pkg?.loc?.path && invoke('open_file', { path: pkg.loc.path })
|
||||
"
|
||||
/>
|
||||
<LinkButton v-if="pkgs.networkStatus === 'online'" :pkg="pkg" />
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Ref, ref } from 'vue';
|
||||
import { Ref, computed, ref } from 'vue';
|
||||
import Button from 'primevue/button';
|
||||
import Divider from 'primevue/divider';
|
||||
import MultiSelect from 'primevue/multiselect';
|
||||
import ToggleSwitch from 'primevue/toggleswitch';
|
||||
@ -40,6 +41,39 @@ const list = () => {
|
||||
empty.value = res.length === 0;
|
||||
return res;
|
||||
};
|
||||
|
||||
const shouldShowRecommended = computed(() => {
|
||||
if (prf.current!.meta.game === 'ongeki') {
|
||||
return !pkgs.allLocal.some((p) => pkgKey(p) === 'segatools-mu3hook');
|
||||
}
|
||||
if (prf.current!.meta.game === 'chunithm') {
|
||||
return (
|
||||
!pkgs.allLocal.some((p) => pkgKey(p) === 'segatools-chusanhook') ||
|
||||
!pkgs.allLocal.some((p) => pkgKey(p) === 'mempatcher-mempatcher')
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const getRecommendedTooltip = () => {
|
||||
if (prf.current!.meta.game === 'ongeki') {
|
||||
return 'segatools-mu3hook';
|
||||
}
|
||||
if (prf.current!.meta.game === 'chunithm') {
|
||||
return 'segatools-chusanhook and mempatcher';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const installRecommended = () => {
|
||||
if (prf.current!.meta.game === 'ongeki') {
|
||||
pkgs.installFromKey('segatools-mu3hook');
|
||||
}
|
||||
if (prf.current!.meta.game === 'chunithm') {
|
||||
pkgs.installFromKey('segatools-chusanhook');
|
||||
pkgs.installFromKey('mempatcher-mempatcher');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -78,6 +112,14 @@ const list = () => {
|
||||
</div>
|
||||
</div>
|
||||
<Divider />
|
||||
<Button
|
||||
v-if="shouldShowRecommended"
|
||||
label="Install recommended packages"
|
||||
v-tooltip="getRecommendedTooltip"
|
||||
icon="pi pi-plus"
|
||||
class="mb-3"
|
||||
@click="installRecommended"
|
||||
/>
|
||||
<div v-for="p in list()" class="flex flex-row">
|
||||
<ModStoreEntry :pkg="p" />
|
||||
</div>
|
||||
|
@ -22,7 +22,6 @@ const prf = usePrfStore();
|
||||
icon="pi pi-plus"
|
||||
class="chunithm-button profile-button"
|
||||
@click="() => prf.create('chunithm')"
|
||||
v-tooltip="'!!! Experimental !!!'"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-12 flex flex-col flex-wrap align-middle gap-4">
|
||||
|
@ -2,10 +2,12 @@
|
||||
import { ref } from 'vue';
|
||||
import Button from 'primevue/button';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import * as path from '@tauri-apps/api/path';
|
||||
import { invoke } from '../invoke';
|
||||
import { usePrfStore } from '../stores';
|
||||
import { useGeneralStore, usePrfStore } from '../stores';
|
||||
import { ProfileMeta } from '../types';
|
||||
|
||||
const general = useGeneralStore();
|
||||
const prf = usePrfStore();
|
||||
const isEditing = ref(false);
|
||||
|
||||
@ -110,7 +112,7 @@ const deleteProfile = async () => {
|
||||
style="width: 2rem; height: 2rem"
|
||||
@click="isEditing = true"
|
||||
/>
|
||||
<!-- <Button
|
||||
<Button
|
||||
rounded
|
||||
icon="pi pi-folder"
|
||||
severity="help"
|
||||
@ -121,9 +123,13 @@ const deleteProfile = async () => {
|
||||
@click="
|
||||
path
|
||||
.join(general.dataDir, `profile-${p!.game}-${p!.name}`)
|
||||
.then(open)
|
||||
.then(async (path) => {
|
||||
if (await invoke('file_exists', { path })) {
|
||||
await invoke('open_file', { path });
|
||||
}
|
||||
})
|
||||
"
|
||||
/> -->
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -26,7 +26,7 @@ const startline = async (force: boolean, refresh: boolean) => {
|
||||
} else if ('MissingLocalPackage' in o) {
|
||||
return `Package missing: ${o.MissingLocalPackage}`;
|
||||
} else if ('MissingDependency' in o) {
|
||||
return `Dependency missing: ${o.MissingDependency}`;
|
||||
return `Dependency missing: ${(o.MissingDependency as string[]).join(' ')}`;
|
||||
} else if ('MissingTool' in o) {
|
||||
return `Tool missing: ${o.MissingTool}`;
|
||||
} else {
|
||||
|
@ -119,6 +119,7 @@ const checkSegatoolsIni = async (target: string) => {
|
||||
return { title: pkgKey(p), value: pkgKey(p) };
|
||||
})
|
||||
"
|
||||
placeholder="none"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
></Select>
|
||||
|
@ -25,6 +25,15 @@ const updatesModel = computed({
|
||||
await client.setAutoupdates(value);
|
||||
},
|
||||
});
|
||||
|
||||
const verboseModel = computed({
|
||||
get() {
|
||||
return client.verbose;
|
||||
},
|
||||
async set(value: boolean) {
|
||||
await client.setVerbose(value);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -45,12 +54,18 @@ const updatesModel = computed({
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Offline mode"
|
||||
tooltip="Disables the package store. Requires a restart."
|
||||
tooltip="Disables the package store. Applies after a restart."
|
||||
>
|
||||
<ToggleSwitch v-model="offlineModel" />
|
||||
</OptionRow>
|
||||
<OptionRow title="Enable automatic updates">
|
||||
<ToggleSwitch v-model="updatesModel" />
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Enable detailed logs"
|
||||
tooltip="Applies after a restart."
|
||||
>
|
||||
<ToggleSwitch v-model="verboseModel" />
|
||||
</OptionRow>
|
||||
</OptionCategory>
|
||||
</template>
|
||||
|
@ -193,8 +193,17 @@ export const usePkgStore = defineStore('pkg', {
|
||||
pkg.js.busy = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//if (rv === 'Deferred') { /* download progress */ }
|
||||
async installFromKey(key: string) {
|
||||
try {
|
||||
await invoke('install_package', {
|
||||
key,
|
||||
force: true,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
},
|
||||
|
||||
async updateAll() {
|
||||
@ -346,6 +355,7 @@ export const useClientStore = defineStore('client', () => {
|
||||
|
||||
const offlineMode = ref(false);
|
||||
const enableAutoupdates = ref(true);
|
||||
const verbose = ref(false);
|
||||
|
||||
const scaleValue = (value: ScaleType) =>
|
||||
value === 's' ? 1 : value === 'm' ? 1.25 : value === 'l' ? 1.5 : 2;
|
||||
@ -401,11 +411,15 @@ export const useClientStore = defineStore('client', () => {
|
||||
}
|
||||
|
||||
offlineMode.value = await invoke('get_global_config', {
|
||||
field: 'OfflineMode',
|
||||
field: 'offline_mode',
|
||||
});
|
||||
|
||||
enableAutoupdates.value = await invoke('get_global_config', {
|
||||
field: 'EnableAutoupdates',
|
||||
field: 'enable_autoupdates',
|
||||
});
|
||||
|
||||
verbose.value = await invoke('get_global_config', {
|
||||
field: 'verbose',
|
||||
});
|
||||
};
|
||||
|
||||
@ -438,17 +452,22 @@ export const useClientStore = defineStore('client', () => {
|
||||
|
||||
const setOfflineMode = async (value: boolean) => {
|
||||
offlineMode.value = value;
|
||||
await invoke('set_global_config', { field: 'OfflineMode', value });
|
||||
await invoke('set_global_config', { field: 'offline_mode', value });
|
||||
};
|
||||
|
||||
const setAutoupdates = async (value: boolean) => {
|
||||
enableAutoupdates.value = value;
|
||||
await invoke('set_global_config', {
|
||||
field: 'EnableAutoupdates',
|
||||
field: 'enable_autoupdates',
|
||||
value,
|
||||
});
|
||||
};
|
||||
|
||||
const setVerbose = async (value: boolean) => {
|
||||
verbose.value = value;
|
||||
await invoke('set_global_config', { field: 'verbose', value });
|
||||
};
|
||||
|
||||
getCurrentWindow().onResized(async ({ payload }) => {
|
||||
// For whatever reason this is 0 when minimized
|
||||
if (payload.width > 0) {
|
||||
@ -460,6 +479,7 @@ export const useClientStore = defineStore('client', () => {
|
||||
scaleFactor,
|
||||
offlineMode,
|
||||
enableAutoupdates,
|
||||
verbose,
|
||||
timeout,
|
||||
scaleModel,
|
||||
load,
|
||||
@ -467,5 +487,6 @@ export const useClientStore = defineStore('client', () => {
|
||||
queueSave,
|
||||
setOfflineMode,
|
||||
setAutoupdates,
|
||||
setVerbose,
|
||||
};
|
||||
});
|
||||
|
Reference in New Issue
Block a user