Help Modal
This commit is contained in:
1 parent
8e8aa5f27c
commit
8d987d1526
11 files changed
+2405
-58
No files matched your search
@@ -5,7 +5,6 @@ import {
|
||||
PhFolder,
|
||||
PhFrameCorners,
|
||||
PhMoon,
|
||||
PhPalette,
|
||||
PhSlidersHorizontal,
|
||||
PhSun,
|
||||
PhX,
|
||||
@@ -18,7 +17,8 @@ import { useTooltipPref } from '@/composables/useTooltipPref'
|
||||
import { useAssetThumbsPref } from '@/composables/useAssetThumbsPref'
|
||||
import { showLocked } from '@/composables/useShowLocked'
|
||||
import { Modal, confirmDialog, promptDialog } from '@/components/ui'
|
||||
import { t } from '@/i18n'
|
||||
import { setLocale, t, useI18n } from '@/i18n'
|
||||
import type { Locale } from '@/i18n'
|
||||
|
||||
const props = defineProps<{ modelValue: boolean }>()
|
||||
const emit = defineEmits<{ 'update:modelValue': [value: boolean] }>()
|
||||
@@ -28,6 +28,7 @@ const clipart = useClipartStore()
|
||||
const { theme, setTheme } = useTheme()
|
||||
const { enabled: tooltipsEnabled } = useTooltipPref()
|
||||
const { enabled: thumbsEnabled } = useAssetThumbsPref()
|
||||
const { locale } = useI18n()
|
||||
|
||||
// Re-check the stored clipart folder whenever settings opens — covers the
|
||||
// case where initial App.vue init raced ahead of IDB or the user opens
|
||||
@@ -39,13 +40,12 @@ watch(
|
||||
},
|
||||
)
|
||||
|
||||
type TabId = 'document' | 'clipart' | 'interface' | 'theme'
|
||||
type TabId = 'document' | 'clipart' | 'interface'
|
||||
|
||||
const tabs: { id: TabId; label: string; icon: typeof PhFrameCorners }[] = [
|
||||
{ id: 'document', label: t('settings.tab.document'), icon: PhFrameCorners },
|
||||
{ id: 'clipart', label: t('settings.tab.clipart'), icon: PhFolder },
|
||||
{ id: 'interface', label: t('settings.tab.interface'), icon: PhSlidersHorizontal },
|
||||
{ id: 'theme', label: t('settings.tab.theme'), icon: PhPalette },
|
||||
const tabs: { id: TabId; labelKey: string; icon: typeof PhFrameCorners }[] = [
|
||||
{ id: 'document', labelKey: 'settings.tab.document', icon: PhFrameCorners },
|
||||
{ id: 'clipart', labelKey: 'settings.tab.clipart', icon: PhFolder },
|
||||
{ id: 'interface', labelKey: 'settings.tab.interface', icon: PhSlidersHorizontal },
|
||||
]
|
||||
|
||||
const activeTab = ref<TabId>('document')
|
||||
@@ -195,6 +195,11 @@ const fps = computed({
|
||||
set: (v: number) => store.setFps(v),
|
||||
})
|
||||
|
||||
const selectedLocale = computed({
|
||||
get: () => locale.value,
|
||||
set: (value: Locale) => setLocale(value),
|
||||
})
|
||||
|
||||
const presets: { label: string; w: number; h: number }[] = [
|
||||
{ label: '16:9 · 1280×720', w: 1280, h: 720 },
|
||||
{ label: '16:9 · 1920×1080', w: 1920, h: 1080 },
|
||||
@@ -221,6 +226,15 @@ function applyTheme(t: Theme) {
|
||||
:model-value="modelValue"
|
||||
:title="t('settings.title')"
|
||||
size="lg"
|
||||
:dialog-style="{
|
||||
width: 'min(920px, calc(100% - 32px))',
|
||||
height: 'min(620px, calc(100vh - 48px))',
|
||||
}"
|
||||
:body-style="{
|
||||
display: 'flex',
|
||||
overflow: 'hidden',
|
||||
minHeight: 0,
|
||||
}"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
>
|
||||
<div class="body">
|
||||
@@ -233,7 +247,7 @@ function applyTheme(t: Theme) {
|
||||
@click="activeTab = tab.id"
|
||||
>
|
||||
<component :is="tab.icon" :size="14" />
|
||||
{{ tab.label }}
|
||||
{{ t(tab.labelKey) }}
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
@@ -362,6 +376,18 @@ function applyTheme(t: Theme) {
|
||||
|
||||
<section v-else-if="activeTab === 'interface'">
|
||||
<h3>{{ t('settings.tab.interface') }}</h3>
|
||||
<div class="setting-block">
|
||||
<div class="setting-heading">{{ t('settings.languageTitle') }}</div>
|
||||
<div class="setting-desc">{{ t('settings.languageDesc') }}</div>
|
||||
<label class="setting-select">
|
||||
<span class="setting-label">{{ t('settings.languageLabel') }}</span>
|
||||
<select v-model="selectedLocale">
|
||||
<option value="en">{{ t('settings.languageEnglish') }}</option>
|
||||
<option value="de">{{ t('settings.languageGerman') }}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label class="row-toggle">
|
||||
<span class="switch" :class="{ on: tooltipsEnabled }">
|
||||
<input type="checkbox" v-model="tooltipsEnabled" />
|
||||
@@ -391,41 +417,41 @@ function applyTheme(t: Theme) {
|
||||
<div class="row-toggle-desc">{{ t('settings.assetThumbsDesc') }}</div>
|
||||
</div>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section v-else-if="activeTab === 'theme'">
|
||||
<h3>{{ t('settings.tab.theme') }}</h3>
|
||||
<div class="theme-grid">
|
||||
<button
|
||||
class="theme-card"
|
||||
:class="{ active: theme === 'dark' }"
|
||||
@click="applyTheme('dark')"
|
||||
>
|
||||
<div class="theme-preview theme-preview-dark">
|
||||
<div class="tp-panel"></div>
|
||||
<div class="tp-accent"></div>
|
||||
</div>
|
||||
<div class="theme-label">
|
||||
<PhMoon :size="14" />
|
||||
<span>{{ t('settings.themeDark') }}</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
class="theme-card"
|
||||
:class="{ active: theme === 'light' }"
|
||||
@click="applyTheme('light')"
|
||||
>
|
||||
<div class="theme-preview theme-preview-light">
|
||||
<div class="tp-panel"></div>
|
||||
<div class="tp-accent"></div>
|
||||
</div>
|
||||
<div class="theme-label">
|
||||
<PhSun :size="14" />
|
||||
<span>{{ t('settings.themeLight') }}</span>
|
||||
</div>
|
||||
</button>
|
||||
<div class="setting-block">
|
||||
<div class="setting-heading">{{ t('settings.appearanceTitle') }}</div>
|
||||
<div class="setting-desc">{{ t('settings.themeHint') }}</div>
|
||||
<div class="theme-grid">
|
||||
<button
|
||||
class="theme-card"
|
||||
:class="{ active: theme === 'dark' }"
|
||||
@click="applyTheme('dark')"
|
||||
>
|
||||
<div class="theme-preview theme-preview-dark">
|
||||
<div class="tp-panel"></div>
|
||||
<div class="tp-accent"></div>
|
||||
</div>
|
||||
<div class="theme-label">
|
||||
<PhMoon :size="14" />
|
||||
<span>{{ t('settings.themeDark') }}</span>
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
class="theme-card"
|
||||
:class="{ active: theme === 'light' }"
|
||||
@click="applyTheme('light')"
|
||||
>
|
||||
<div class="theme-preview theme-preview-light">
|
||||
<div class="tp-panel"></div>
|
||||
<div class="tp-accent"></div>
|
||||
</div>
|
||||
<div class="theme-label">
|
||||
<PhSun :size="14" />
|
||||
<span>{{ t('settings.themeLight') }}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hint">{{ t('settings.themeHint') }}</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@@ -438,7 +464,8 @@ function applyTheme(t: Theme) {
|
||||
<style scoped>
|
||||
.body {
|
||||
display: flex;
|
||||
min-height: 340px;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
@@ -450,6 +477,7 @@ function applyTheme(t: Theme) {
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
background: var(--panel-alt);
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.tab {
|
||||
@@ -478,6 +506,7 @@ function applyTheme(t: Theme) {
|
||||
padding: 16px 20px;
|
||||
overflow: auto;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
section {
|
||||
@@ -605,6 +634,56 @@ input[type='number'] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.setting-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
background: var(--panel-alt);
|
||||
}
|
||||
|
||||
.setting-heading {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.setting-desc {
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.setting-select {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
flex: 0 0 auto;
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.setting-label {
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
.setting-select select {
|
||||
width: 100%;
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
padding: 8px 10px;
|
||||
border-radius: var(--radius-sm);
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
}
|
||||
|
||||
/* Switch + two-line label. Defensive resets defeat the scoped
|
||||
blanket `label { flex-direction: column; text-transform: uppercase;
|
||||
font-size: 10px; flex: 1 1 0; }` that form-row labels in this file
|
||||
|
||||
Reference in new issue
Block a user