Add theme presets, gradient backgrounds, and macOS-style window chrome
Replaces the old light/dark toggle with 10 fixed color themes (plus Black & Grey and White) that drive a coordinated gradient palette across the whole admin UI, a Google Fonts picker, and a native-page dark-mode stylesheet that now covers modals and plugin/theme card grids. Fixes several background/gradient seams where the sidebar, toolbar, and content frame were each painting their own independent copy of the page gradient instead of sharing one continuous plane. Also adds a macOS-style traffic-light title bar to every page (close, minimize, maximize) and a persistent bottom taskbar so a minimized page can be restored from anywhere, plus a Plugins management page. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
db07e21f9e
commit
4a547d2c50
@@ -1,6 +1,10 @@
|
||||
<script setup>
|
||||
import { computed, reactive } from 'vue';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import Toggle from '@/components/Toggle.vue';
|
||||
import { themePresets, buildPalette } from '@/pages/settings/themePresets.js';
|
||||
import { googleFonts } from '@/pages/settings/googleFonts.js';
|
||||
|
||||
const swatchPalette = (preset) => buildPalette(preset);
|
||||
|
||||
const props = defineProps({
|
||||
field: { type: Object, required: true },
|
||||
@@ -66,6 +70,24 @@ const pickImage = () => {
|
||||
});
|
||||
frame.open();
|
||||
};
|
||||
|
||||
// Font picker (searchable Google Font dropdown) ----------------------------
|
||||
const fontQuery = ref('');
|
||||
const fontOpen = ref(false);
|
||||
const filteredFonts = computed(() => {
|
||||
const q = fontQuery.value.trim().toLowerCase();
|
||||
const list = q ? googleFonts.filter((f) => f.toLowerCase().includes(q)) : googleFonts;
|
||||
return list.slice(0, 40);
|
||||
});
|
||||
const pickFont = (name) => {
|
||||
emit('update:modelValue', name);
|
||||
fontQuery.value = name;
|
||||
fontOpen.value = false;
|
||||
};
|
||||
const openFontList = () => {
|
||||
fontQuery.value = props.modelValue || '';
|
||||
fontOpen.value = true;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -169,6 +191,59 @@ const pickImage = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Theme picker (10 fixed color presets, whole-UI preview) -->
|
||||
<div v-else-if="field.type === 'theme-picker'" class="field__theme">
|
||||
<button
|
||||
v-for="preset in themePresets"
|
||||
:key="preset.id"
|
||||
type="button"
|
||||
class="field__theme-card"
|
||||
:class="{ 'field__theme-card--active': (val || field.default) === preset.id }"
|
||||
@click="val = preset.id"
|
||||
>
|
||||
<span
|
||||
class="field__theme-swatch"
|
||||
:style="{
|
||||
background: swatchPalette(preset)['--aurora-app-bg'],
|
||||
borderColor: swatchPalette(preset)['--aurora-border'],
|
||||
}"
|
||||
>
|
||||
<span
|
||||
class="field__theme-swatch-accent"
|
||||
:style="{ background: swatchPalette(preset)['--aurora-accent'] }"
|
||||
/>
|
||||
</span>
|
||||
<span class="field__theme-name">{{ preset.label }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Font picker (searchable Google Font dropdown) -->
|
||||
<div v-else-if="field.type === 'font-picker'" class="field__font">
|
||||
<input
|
||||
type="text"
|
||||
class="field__input"
|
||||
placeholder="Search Google Fonts…"
|
||||
:value="fontOpen ? fontQuery : (val || 'Default')"
|
||||
@focus="openFontList"
|
||||
@input="fontQuery = $event.target.value"
|
||||
@blur="() => setTimeout(() => (fontOpen = false), 150)"
|
||||
/>
|
||||
<div v-if="fontOpen" class="field__font-list">
|
||||
<button
|
||||
v-for="name in filteredFonts"
|
||||
:key="name"
|
||||
type="button"
|
||||
class="field__font-item"
|
||||
:class="{ 'field__font-item--active': val === name }"
|
||||
:style="{ fontFamily: `'${name}', sans-serif` }"
|
||||
@mousedown.prevent="pickFont(name)"
|
||||
>
|
||||
{{ name }}
|
||||
</button>
|
||||
<p v-if="!filteredFonts.length" class="field__multi-empty">No fonts match.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Repeater -->
|
||||
<div v-else-if="field.type === 'repeater'" class="field__repeater">
|
||||
<div v-for="(row, i) in rows" :key="i" class="field__repeater-row">
|
||||
@@ -309,6 +384,49 @@ const pickImage = () => {
|
||||
.field__sites-user { color: var(--aurora-text-muted); }
|
||||
.field__sites-help { margin: 10px 0 0; font-size: 0.75rem; color: var(--aurora-text-muted); }
|
||||
|
||||
/* Theme picker */
|
||||
.field__theme { display: flex; flex-wrap: wrap; gap: 10px; width: 100%; }
|
||||
.field__theme-card {
|
||||
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
||||
width: 76px; padding: 10px 6px;
|
||||
border: 1px solid var(--aurora-border);
|
||||
border-radius: 10px;
|
||||
background: var(--aurora-bg-subtle);
|
||||
cursor: pointer;
|
||||
}
|
||||
.field__theme-card--active { border-color: var(--aurora-accent); box-shadow: 0 0 0 1px var(--aurora-accent); }
|
||||
.field__theme-swatch {
|
||||
width: 36px; height: 36px; border-radius: 10px;
|
||||
border: 1px solid var(--aurora-card-border);
|
||||
display: flex; align-items: flex-end; justify-content: flex-end;
|
||||
padding: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.field__theme-swatch-accent {
|
||||
width: 10px; height: 10px; border-radius: 50%;
|
||||
}
|
||||
.field__theme-name { font-size: 0.6875rem; color: var(--aurora-text-muted); }
|
||||
|
||||
/* Font picker */
|
||||
.field__font { position: relative; width: 100%; max-width: 320px; }
|
||||
.field__font-list {
|
||||
position: absolute; top: calc(100% + 4px); left: 0; right: 0;
|
||||
max-height: 260px; overflow-y: auto; z-index: 20;
|
||||
background: var(--aurora-card-bg);
|
||||
border: 1px solid var(--aurora-border);
|
||||
border-radius: 10px;
|
||||
padding: 6px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.field__font-item {
|
||||
display: block; width: 100%; text-align: left;
|
||||
border: none; background: none; cursor: pointer;
|
||||
padding: 8px 10px; border-radius: 7px;
|
||||
font-size: 0.875rem; color: var(--aurora-text);
|
||||
}
|
||||
.field__font-item:hover { background: var(--aurora-bg-subtle); }
|
||||
.field__font-item--active { color: var(--aurora-accent); }
|
||||
|
||||
@media (max-width: 782px) {
|
||||
.field { grid-template-columns: 1fr; gap: 12px; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user