Update Navbar and Settings-Styling

This commit is contained in:
felix 2025-10-15 18:32:57 +02:00
parent 9801258b1c
commit e5c15053d9
4 changed files with 308 additions and 242 deletions

View file

@ -1,169 +0,0 @@
<template>
<div class="columns is-multiline">
<div class="column is-12 mt-2">
<div class="card">
<header class="card-header">
<p class="card-header-title">WebUI Settings</p>
<span class="card-header-icon">
<span class="icon"><i class="fas fa-cog" /></span>
</span>
</header>
<div class="card-content">
<div class="columns is-multiline">
<div class="column is-6">
<div class="field">
<label class="label is-unselectable">
<span class="icon"><i class="fa-solid fa-computer" /></span>
Page View
</label>
<div class="control">
<div class="control">
<input id="view_mode" type="checkbox" class="switch is-success" v-model="simpleMode">
<label for="view_mode" class="is-unselectable">
&nbsp;{{ simpleMode ? 'Simple View' : 'Regular View' }}
</label>
</div>
</div>
<p class="help is-bold">
<span class="icon"> <i class="fa-solid fa-info-circle" /></span>
The simple view is ideal for non-technical users and mobile devices.
</p>
</div>
<div class="field">
<label class="label is-unselectable">Color scheme</label>
<div class="control">
<label for="auto" class="radio">
<input id="auto" type="radio" v-model="selectedTheme" value="auto">
<span class="icon"><i class="fa-solid fa-circle-half-stroke" /></span>
<span>Auto</span>
</label>
<label for="light" class="radio">
<input id="light" type="radio" v-model="selectedTheme" value="light">
<span class="icon has-text-warning"><i class="fa-solid fa-sun" /></span>
<span>Light</span>
</label>
<label for="dark" class="radio">
<input id="dark" type="radio" v-model="selectedTheme" value="dark">
<span class="icon"><i class="fa-solid fa-moon" /></span>
<span>Dark</span>
</label>
</div>
</div>
<div class="field">
<label class="label is-unselectable">
Show Background
<template v-if="bg_enable">
<NuxtLink @click="$emit('reload_bg')" class="is-bold">Reload</NuxtLink>
<span class="icon" v-if="isLoading"><i class="fa fa-spin fa-spinner" /></span>
</template>
</label>
<div class="control">
<input id="random_bg" type="checkbox" class="switch is-success" v-model="bg_enable">
<label for="random_bg" class="is-unselectable">
&nbsp;{{ bg_enable ? 'Yes' : 'No' }}
</label>
</div>
</div>
<div class="field">
<label class="label is-unselectable" for="random_bg_opacity">
Background visibility <code>{{ parseFloat(String(1.0 - bg_opacity)).toFixed(2) }}</code>
</label>
<div class="control">
<input id="random_bg_opacity" style="width: 100%" type="range" v-model="bg_opacity" min="0.50"
max="1.00" step="0.05">
</div>
</div>
<div class="field" v-if="!simpleMode">
<label class="label is-unselectable" for="show_thumbnail">URLs Separator</label>
<div class="control">
<div class="select is-fullwidth">
<select class="is-fullwidth" v-model="separator">
<option v-for="(sep, index) in separators" :key="`sep-${index}`" :value="sep.value">
{{ sep.name }} ({{ sep.value }})
</option>
</select>
</div>
</div>
</div>
</div>
<div class="column is-6">
<div class="field">
<label class="label is-unselectable" for="show_thumbnail">Show Thumbnails</label>
<div class="control">
<input id="show_thumbnail" type="checkbox" class="switch is-success" v-model="show_thumbnail">
<label for="show_thumbnail" class="is-unselectable">
&nbsp;{{ show_thumbnail ? 'Yes' : 'No' }}
</label>
</div>
<p class="help is-bold"> Show videos thumbnail if available.</p>
</div>
<div class="field">
<label class="label" for="allow_toasts">Show notifications</label>
<div class="control">
<input id="allow_toasts" type="checkbox" class="switch is-success" v-model="allow_toasts">
<label for="allow_toasts" class="is-unselectable">
&nbsp;{{ allow_toasts ? 'Yes' : 'No' }}
</label>
</div>
</div>
<div class="field" v-if="allow_toasts">
<label class="label">Notifications position</label>
<div class="control">
<div class="select is-fullwidth">
<select v-model="toast_position">
<option :value="POSITION.TOP_RIGHT">{{ POSITION.TOP_RIGHT }}</option>
<option :value="POSITION.TOP_CENTER">{{ POSITION.TOP_CENTER }}</option>
<option :value="POSITION.TOP_LEFT">{{ POSITION.TOP_LEFT }}</option>
<option :value="POSITION.BOTTOM_RIGHT">{{ POSITION.BOTTOM_RIGHT }}</option>
<option :value="POSITION.BOTTOM_CENTER">{{ POSITION.BOTTOM_CENTER }}</option>
<option :value="POSITION.BOTTOM_LEFT">{{ POSITION.BOTTOM_LEFT }}</option>
</select>
</div>
</div>
</div>
<div class="field" v-if="allow_toasts">
<label class="label" for="dismiss_on_click">Dismiss notification on click</label>
<div class="control">
<input id="dismiss_on_click" type="checkbox" class="switch is-success"
v-model="toast_dismiss_on_click">
<label for="dismiss_on_click" class="is-unselectable">
&nbsp;{{ toast_dismiss_on_click ? 'Yes' : 'No' }}
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useStorage } from '@vueuse/core'
import { POSITION } from 'vue-toastification'
import { useConfigStore } from '~/stores/ConfigStore'
defineProps<{ isLoading: boolean }>()
defineEmits<{ (e: 'reload_bg'): void }>()
const bg_enable = useStorage<boolean>('random_bg', true)
const bg_opacity = useStorage<number>('random_bg_opacity', 0.95)
const selectedTheme = useStorage<'auto' | 'light' | 'dark'>('theme', 'auto')
const allow_toasts = useStorage<boolean>('allow_toasts', true)
const toast_position = useStorage<POSITION>('toast_position', POSITION.TOP_RIGHT)
const toast_dismiss_on_click = useStorage<boolean>('toast_dismiss_on_click', true)
const show_thumbnail = useStorage<boolean>('show_thumbnail', true)
const separator = useStorage<string>('url_separator', separators[0]?.value ?? ',')
const simpleMode = useStorage<boolean>('simple_mode', useConfigStore().app.simple_mode || false)
</script>

View file

@ -6,9 +6,9 @@
<label class="label" for="download-url">
What you would like to download?
<span class="is-pulled-right">
<span class="icon is-pointer" :class="connectionStatusColor" @click="$emit('show_settings')"
v-tooltip="'WebUI Settings'">
<i class="fas fa-cogs" /></span>
<NuxtLink class="icon is-pointer" to="/settings" @click.prevent="(e: MouseEvent) => changeRoute(e)">
<span class="icon"><i class="fa-solid fa-cog" /></span>
</NuxtLink>
</span>
</label>
<div class="field has-addons">
@ -544,6 +544,15 @@ const showMessage = (item: StoreItem) => {
return (item.msg?.length || 0) > 0
}
const changeRoute = async (_: MouseEvent, callback: (() => void) | null = null) => {
showMenu.value = false
document.querySelectorAll('div.has-dropdown').forEach(el => el.classList.remove('is-active'))
if (callback) {
callback()
}
}
const connectionStatusColor = computed(() => {
switch (socketStore.connectionStatus) {
case 'connected':

View file

@ -2,14 +2,7 @@
<template v-if="simpleMode">
<Connection :status="socket.connectionStatus" @reconnect="() => socket.reconnect()" />
<Simple @show_settings="() => show_settings = true" />
</template>
<template v-if="show_settings">
<Modal @close="show_settings = false"
:content-class="isMobile ? 'modal-content-max is-overflow-scroll ' : 'modal-content-max is-overflow-visible'">
<Settings v-if="show_settings" :isLoading="loadingImage" @reload_bg="() => loadImage(true)" />
</Modal>
<Simple />
</template>
<template v-if="!simpleMode">
@ -23,9 +16,8 @@
<NuxtLink class="navbar-item is-text-overflow" to="/" @click.prevent="(e: MouseEvent) => changeRoute(e)"
v-tooltip="socket.isConnected ? 'Connected' : 'Connecting'">
<span class="is-text-overflow">
<span class="icon">
<i v-if="'connecting' === socket.connectionStatus" class="fas fa-arrows-rotate fa-spin" />
<i v-else class="fas fa-home" />
<span class="icon" v-if="'connecting' === socket.connectionStatus" >
<i class="fas fa-arrows-rotate fa-spin" />
</span>
<span class="has-text-bold" :class="connectionStatusColor">
YTPTube
@ -43,6 +35,12 @@
<div class="navbar-menu is-unselectable" :class="{ 'is-active': showMenu }">
<div class="navbar-start">
<NuxtLink class="navbar-item" to="/" @click.prevent="(e: MouseEvent) => changeRoute(e)">
<span class="icon"><i class="fa-solid fa-home" /></span>
<span>Home</span>
</NuxtLink>
<NuxtLink class="navbar-item" to="/browser" @click.prevent="(e: MouseEvent) => changeRoute(e)">
<span class="icon"><i class="fa-solid fa-folder-tree" /></span>
<span>Files</span>
@ -52,23 +50,16 @@
<span class="icon"><i class="fa-solid fa-sliders" /></span>
<span>Presets</span>
</NuxtLink>
<div class="navbar-item has-dropdown">
<a class="navbar-link" @click="(e: MouseEvent) => openMenu(e)">
<span class="icon"><i class="fas fa-tasks" /></span>
<span>Tasks</span>
</a>
<div class="navbar-dropdown">
<NuxtLink class="navbar-item" to="/tasks" @click.prevent="(e: MouseEvent) => changeRoute(e)">
<span class="icon"><i class="fa-solid fa-tasks" /></span>
<span>List</span>
</NuxtLink>
<NuxtLink class="navbar-item" to="/task_definitions" @click.prevent="(e: MouseEvent) => changeRoute(e)">
<span class="icon"><i class="fa-solid fa-diagram-project" /></span>
<span>Definitions</span>
</NuxtLink>
</div>
</div>
<NuxtLink class="navbar-item" to="/tasks" @click.prevent="(e: MouseEvent) => changeRoute(e)">
<span class="icon"><i class="fa-solid fa-tasks" /></span>
<span>Task-List</span>
</NuxtLink>
<NuxtLink class="navbar-item" to="/task_definitions" @click.prevent="(e: MouseEvent) => changeRoute(e)">
<span class="icon"><i class="fa-solid fa-diagram-project" /></span>
<span>Task-Definitions</span>
</NuxtLink>
<NuxtLink class="navbar-item" to="/notifications" @click.prevent="(e: MouseEvent) => changeRoute(e)">
<span class="icon-text">
@ -84,26 +75,6 @@
</div>
<div class="navbar-end">
<div class="navbar-item has-dropdown">
<a class="navbar-link" @click="(e: MouseEvent) => openMenu(e)">
<span class="icon"><i class="fas fa-tools" /></span>
<span>Other</span>
</a>
<div class="navbar-dropdown">
<NuxtLink class="navbar-item" to="/logs" @click.prevent="(e: MouseEvent) => changeRoute(e)"
v-if="config.app.file_logging">
<span class="icon"><i class="fa-solid fa-file-lines" /></span>
<span>Logs</span>
</NuxtLink>
<NuxtLink class="navbar-item" to="/console" @click.prevent="(e: MouseEvent) => changeRoute(e)"
v-if="config.app.console_enabled">
<span class="icon"><i class="fa-solid fa-terminal" /></span>
<span>Console</span>
</NuxtLink>
</div>
</div>
<div class="navbar-item" v-if="true === config.app.is_native">
<button class="button is-dark" @click="shutdownApp">
@ -112,29 +83,13 @@
</button>
</div>
<div class="navbar-item">
<button class="button is-dark" @click="reloadPage">
<span class="icon"><i class="fas fa-refresh" /></span>
<span v-if="isMobile">Reload</span>
</button>
</div>
<NuxtLink class="navbar-item" to="/settings" @click.prevent="(e: MouseEvent) => changeRoute(e)">
<span class="icon"><i class="fa-solid fa-cog" /></span>
<span>Settings</span>
</NuxtLink>
<NotifyDropdown />
<div class="navbar-item" v-if="!isMobile">
<button class="button is-dark has-tooltip-bottom mr-4" v-tooltip.bottom="'WebUI Settings'"
@click="show_settings = !show_settings">
<span class="icon"><i class="fas fa-cog" /></span>
</button>
</div>
<div class="navbar-item" v-if="isMobile">
<button class="button is-dark" @click="show_settings = !show_settings">
<span class="icon"><i class="fas fa-cog" /></span>
<span>WebUI Settings</span>
</button>
</div>
</div>
</div>
</nav>
@ -203,7 +158,6 @@ const selectedTheme = useStorage('theme', 'auto')
const socket = useSocketStore()
const config = useConfigStore()
const loadedImage = ref()
const show_settings = ref(false)
const loadingImage = ref(false)
const bg_enable = useStorage('random_bg', true)
const bg_opacity = useStorage('random_bg_opacity', 0.95)
@ -315,8 +269,6 @@ watch(selectedTheme, value => {
} catch { }
})
const reloadPage = () => window.location.reload()
watch(bg_enable, async v => await handleImage(v))
watch(bg_opacity, v => {
if (false === bg_enable.value) {

274
ui/app/pages/settings.vue Normal file
View file

@ -0,0 +1,274 @@
<template>
<main class="m-6">
<div>
<span class="title is-4">
<span class="icon-text">
<span class="icon"><i class="fas fa-cog" /></span>
<p class="card-header-title">Settings</p>
</span>
</span>
<!-- Link buttons -->
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label"></label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<NuxtLink class="button is-primary" to="/logs" @click.prevent="(e: MouseEvent) => changeRoute(e)"
v-if="config.app.file_logging">
<span class="icon"><i class="fa-solid fa-file-lines" /></span>
<span>Open Logs</span>
</NuxtLink>
<NuxtLink class="button is-primary" to="/console" @click.prevent="(e: MouseEvent) => changeRoute(e)"
v-if="config.app.console_enabled">
<span class="icon"><i class="fa-solid fa-terminal" /></span>
<span>Open Console</span>
</NuxtLink>
</div>
</div>
</div>
</div>
<!-- Page View -->
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label"><i class="icon fa-solid fa-computer" /> Page View</label>
</div>
<div class="field-body">
<div class="field">
<div class="control is-expanded has-icons-left">
<input id="view_mode" type="checkbox" class="switch is-success" v-model="simpleMode">
<label for="view_mode" class="is-unselectable">
&nbsp;{{ simpleMode ? 'Simple View' : 'Regular View' }}
</label>
</div>
<p class="help">
<span class="icon"> <i class="fa-solid fa-info-circle" /></span>
The simple view is ideal for non-technical users and mobile devices.
</p>
</div>
</div>
</div>
<!-- Color Scheme -->
<div class="field is-horizontal">
<div class="field-label">
<label class="label">Color scheme</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<label for="auto" class="radio">
<input id="auto" type="radio" v-model="selectedTheme" value="auto">
<span class="icon"><i class="fa-solid fa-circle-half-stroke" /></span>
<span>Auto</span>
</label>
<label for="light" class="radio">
<input id="light" type="radio" v-model="selectedTheme" value="light">
<span class="icon has-text-warning"><i class="fa-solid fa-sun" /></span>
<span>Light</span>
</label>
<label for="dark" class="radio">
<input id="dark" type="radio" v-model="selectedTheme" value="dark">
<span class="icon"><i class="fa-solid fa-moon" /></span>
<span>Dark</span>
</label>
</div>
</div>
</div>
</div>
<!-- Show Background -->
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Show Background</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input id="random_bg" type="checkbox" class="switch is-success" v-model="bg_enable">
<label for="random_bg" class="is-unselectable">
&nbsp;{{ bg_enable ? 'Yes' : 'No' }}
</label>
</div>
</div>
<div class="field">
<template v-if="bg_enable">
<NuxtLink @click="$emit('reload_bg')" class="is-bold">Reload</NuxtLink>
<span class="icon" v-if="isLoading"><i class="fa fa-spin fa-spinner" /></span>
</template>
</div>
</div>
</div>
<!-- Background visibility -->
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Background visibility</label>
</div>
<div class="field-body">
<div class="field is-expanded">
<a class="button is-static">
<code>{{ parseFloat(String(1.0 - bg_opacity)).toFixed(2) }}</code>
</a>
<div class="control">
<input id="random_bg_opacity" style="width: 100%" type="range" v-model="bg_opacity" min="0.50"
max="1.00" step="0.05">
</div>
</div>
</div>
</div>
<!-- URL Separator -->
<div class="field is-horizontal" v-if="!simpleMode">
<div class="field-label is-normal">
<label class="label">URL Separator</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<div class="select is-fullwidth">
<select v-model="separator">
<option v-for="(sep, index) in separators" :key="`sep-${index}`" :value="sep.value">
{{ sep.name }} ({{ sep.value }})
</option>
</select>
</div>
</div>
</div>
</div>
</div>
<!-- Show Thumbnails -->
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Show Thumbnails</label>
</div>
<div class="field-body">
<div class="control">
<div class="field">
<input id="show_thumbnail" type="checkbox" class="switch is-success" v-model="show_thumbnail">
<label for="show_thumbnail" class="is-unselectable">
&nbsp;{{ show_thumbnail ? 'Yes' : 'No' }}
</label>
</div>
<p class="help">
<span class="icon"><i class="fa-solid fa-info-circle"/></span>
Show videos thumbnail if available
</p>
</div>
</div>
</div>
<!-- Show notifications -->
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Show notifications</label>
</div>
<div class="field-body">
<div class="control">
<div class="field">
<input id="allow_toasts" type="checkbox" class="switch is-success" v-model="allow_toasts">
<label for="allow_toasts" class="is-unselectable">
&nbsp;{{ allow_toasts ? 'Yes' : 'No' }}
</label>
</div>
</div>
</div>
</div>
<!-- Notification Position -->
<div class="field is-horizontal" v-if="allow_toasts">
<div class="field-label is-normal">
<label class="label">Notifications position</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
<div class="select is-fullwidth">
<select v-model="toast_position">
<option :value="POSITION.TOP_RIGHT">{{ POSITION.TOP_RIGHT }}</option>
<option :value="POSITION.TOP_CENTER">{{ POSITION.TOP_CENTER }}</option>
<option :value="POSITION.TOP_LEFT">{{ POSITION.TOP_LEFT }}</option>
<option :value="POSITION.BOTTOM_RIGHT">{{ POSITION.BOTTOM_RIGHT }}</option>
<option :value="POSITION.BOTTOM_CENTER">{{ POSITION.BOTTOM_CENTER }}</option>
<option :value="POSITION.BOTTOM_LEFT">{{ POSITION.BOTTOM_LEFT }}</option>
</select>
</div>
</div>
</div>
</div>
</div>
<!-- Dismiss notification on click -->
<div class="field is-horizontal" v-if="allow_toasts">
<div class="field-label is-normal">
<label class="label">Dismiss notification on click</label>
</div>
<div class="field-body">
<div class="control">
<div class="field">
<input id="dismiss_on_click" type="checkbox" class="switch is-success" v-model="toast_dismiss_on_click">
<label for="dismiss_on_click" class="is-unselectable">
&nbsp;{{ toast_dismiss_on_click ? 'Yes' : 'No' }}
</label>
</div>
</div>
</div>
</div>
<!--end-->
</div>
</main>
</template>
<script setup lang="ts">
import { useStorage } from '@vueuse/core'
import { POSITION } from 'vue-toastification'
import { useConfigStore } from '~/stores/ConfigStore'
defineProps<{ isLoading: boolean }>()
defineEmits<{ (e: 'reload_bg'): void }>()
const bg_enable = useStorage<boolean>('random_bg', true)
const bg_opacity = useStorage<number>('random_bg_opacity', 0.95)
const selectedTheme = useStorage<'auto' | 'light' | 'dark'>('theme', 'auto')
const allow_toasts = useStorage<boolean>('allow_toasts', true)
const toast_position = useStorage<POSITION>('toast_position', POSITION.TOP_RIGHT)
const toast_dismiss_on_click = useStorage<boolean>('toast_dismiss_on_click', true)
const show_thumbnail = useStorage<boolean>('show_thumbnail', true)
const separator = useStorage<string>('url_separator', separators[0]?.value ?? ',')
const simpleMode = useStorage<boolean>('simple_mode', useConfigStore().app.simple_mode || false)
const config = useConfigStore()
const changeRoute = async (_: MouseEvent, callback: (() => void) | null = null) => {
document.querySelectorAll('div.has-dropdown').forEach(el => el.classList.remove('is-active'))
if (callback) {
callback()
}
}
</script>