Fix Khanegi custom theme live preview for primary and scheme colors.
Edit the scheme matching the preview dark/light toggle and surface primary in the sample card so custom changes are visible before save. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+62
-22
@@ -40,13 +40,17 @@ const config = reactive(createDefaultKhanegiUiConfig()) as ReturnType<
|
||||
const hasDefaultSnapshot = computed(() => Boolean(config.defaultSnapshot))
|
||||
const isCustom = computed(() => selectedPreset.value === 'custom')
|
||||
|
||||
/** رنگهای همان حالتی که در پیشنمایش دیده میشود (dark/light) */
|
||||
const activePreviewColors = computed(() => {
|
||||
const schemes = config.app.theme.schemes
|
||||
if (schemes?.[previewMode.value]) return schemes[previewMode.value]
|
||||
return config.app.theme.colors
|
||||
})
|
||||
|
||||
const colorKeys = computed(() => Object.keys(config.app.theme.colors))
|
||||
/** فیلدهای رنگ سفارشی روی همان scheme پیشنمایش ویرایش میشوند تا نمونه زنده بماند */
|
||||
const editableColors = computed(() => activePreviewColors.value)
|
||||
|
||||
const colorKeys = computed(() => Object.keys(editableColors.value))
|
||||
|
||||
const previewStyle = computed(() => ({
|
||||
background: activePreviewColors.value.pageBg,
|
||||
@@ -54,6 +58,12 @@ const previewStyle = computed(() => ({
|
||||
borderRadius: `${config.app.theme.borderRadius.lg}px`,
|
||||
}))
|
||||
|
||||
const primaryStyle = computed(() => ({
|
||||
background: activePreviewColors.value.primary,
|
||||
color: activePreviewColors.value.onAccent,
|
||||
borderRadius: `${config.app.theme.borderRadius.md}px`,
|
||||
}))
|
||||
|
||||
const accentStyle = computed(() => ({
|
||||
background: activePreviewColors.value.secondary,
|
||||
color: activePreviewColors.value.onAccent,
|
||||
@@ -64,13 +74,14 @@ function selectPreset(id: KhanegiPresetId) {
|
||||
selectedPreset.value = id
|
||||
if (id === 'custom') {
|
||||
config.app.theme.presetId = 'custom'
|
||||
if (!config.app.theme.schemes) {
|
||||
config.app.theme.schemes = {
|
||||
dark: { ...config.app.theme.colors },
|
||||
light: { ...config.app.theme.colors },
|
||||
}
|
||||
ensureSchemes()
|
||||
// clone so custom edits never mutate shared preset objects
|
||||
config.app.theme.schemes = {
|
||||
dark: { ...config.app.theme.schemes!.dark },
|
||||
light: { ...config.app.theme.schemes!.light },
|
||||
}
|
||||
message.info('حالت سفارشی — میتوانید جزئیات را ویرایش کنید')
|
||||
syncActiveColorsFromMode(config)
|
||||
message.info('حالت سفارشی — تغییرات فوراً در نمونه کارت دیده میشود')
|
||||
return
|
||||
}
|
||||
applyKhanegiPreset(config, id)
|
||||
@@ -82,14 +93,18 @@ function selectPreset(id: KhanegiPresetId) {
|
||||
function onModeChange() {
|
||||
syncActiveColorsFromMode(config)
|
||||
previewMode.value = config.app.theme.mode === 'light' ? 'light' : 'dark'
|
||||
if (isCustom.value) markCustomAndSyncSchemes(config)
|
||||
if (isCustom.value) {
|
||||
config.app.theme.presetId = 'custom'
|
||||
}
|
||||
}
|
||||
|
||||
/** بعد از هر تغییر رنگ/گوشه: سفارشی شو و colors فعال را با scheme حالت اپ همتراز کن */
|
||||
function onColorEdit() {
|
||||
if (!isCustom.value) {
|
||||
selectedPreset.value = 'custom'
|
||||
}
|
||||
markCustomAndSyncSchemes(config)
|
||||
selectedPreset.value = 'custom'
|
||||
config.app.theme.presetId = 'custom'
|
||||
ensureSchemes()
|
||||
const appMode = config.app.theme.mode === 'light' ? 'light' : 'dark'
|
||||
Object.assign(config.app.theme.colors, config.app.theme.schemes![appMode])
|
||||
}
|
||||
|
||||
function syncAdvancedJson() {
|
||||
@@ -310,14 +325,18 @@ onMounted(load)
|
||||
<button type="button" :class="{ on: previewMode === 'dark' }" @click="previewMode = 'dark'">تیره</button>
|
||||
<button type="button" :class="{ on: previewMode === 'light' }" @click="previewMode = 'light'">روشن</button>
|
||||
</div>
|
||||
<span class="preview-chip" :style="accentStyle">اکسنت</span>
|
||||
</div>
|
||||
<div
|
||||
class="preview-primary-strip"
|
||||
:style="{ background: activePreviewColors.primary, borderRadius: config.app.theme.borderRadius.sm + 'px' }"
|
||||
/>
|
||||
<div
|
||||
class="preview-card"
|
||||
:style="{
|
||||
background: activePreviewColors.surface,
|
||||
borderRadius: config.app.theme.borderRadius.md + 'px',
|
||||
color: activePreviewColors.textPrimary,
|
||||
border: `1px solid ${activePreviewColors.border}`,
|
||||
}"
|
||||
>
|
||||
<div style="opacity: 0.7; font-size: 12px">surface</div>
|
||||
@@ -325,6 +344,10 @@ onMounted(load)
|
||||
<div style="margin-top: 6px; font-size: 12px" :style="{ color: activePreviewColors.textSecondary }">
|
||||
متن فرعی با رنگ theme
|
||||
</div>
|
||||
<div class="preview-actions">
|
||||
<span class="preview-chip" :style="primaryStyle">اصلی</span>
|
||||
<span class="preview-chip" :style="accentStyle">اکسنت</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -348,27 +371,30 @@ onMounted(load)
|
||||
|
||||
<section class="form-section">
|
||||
<h3>رنگها {{ isCustom ? '(سفارشی)' : '(فقطخواندنی تا انتخاب سفارشی)' }}</h3>
|
||||
<p v-if="isCustom" class="page-sub" style="margin-bottom: 12px">
|
||||
ویرایش برای حالت «{{ previewMode === 'dark' ? 'تیره' : 'روشن' }}» پیشنمایش است؛ با سوییچ تیره/روشن بالای کارت، هر پالت جداگانه تنظیم میشود.
|
||||
</p>
|
||||
<div class="color-grid">
|
||||
<label v-for="key in colorKeys" :key="key" class="color-field">
|
||||
<span>{{ colorFieldLabels[key] || key }}</span>
|
||||
<label v-for="key in colorKeys" :key="`${previewMode}-${key}`" class="color-field">
|
||||
<span>{{ colorFieldLabels[key] || key }} · {{ previewMode === 'dark' ? 'تیره' : 'روشن' }}</span>
|
||||
<div class="color-row">
|
||||
<span
|
||||
class="color-swatch"
|
||||
:style="{ background: (config.app.theme.colors as any)[key] }"
|
||||
:style="{ background: (editableColors as any)[key] }"
|
||||
/>
|
||||
<input
|
||||
v-if="String((config.app.theme.colors as any)[key] || '').startsWith('#')"
|
||||
v-model="(config.app.theme.colors as any)[key]"
|
||||
v-if="String((editableColors as any)[key] || '').startsWith('#')"
|
||||
v-model="(editableColors as any)[key]"
|
||||
type="color"
|
||||
class="color-native"
|
||||
:disabled="!isCustom"
|
||||
@change="onColorEdit"
|
||||
@input="onColorEdit"
|
||||
/>
|
||||
<a-input
|
||||
v-model:value="(config.app.theme.colors as any)[key]"
|
||||
v-model:value="(editableColors as any)[key]"
|
||||
dir="ltr"
|
||||
:disabled="!isCustom"
|
||||
@change="onColorEdit"
|
||||
@update:value="onColorEdit"
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
@@ -581,6 +607,11 @@ onMounted(load)
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
|
||||
.preview-primary-strip {
|
||||
height: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.preview-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -617,8 +648,17 @@ onMounted(load)
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.preview-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 14px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.preview-chip {
|
||||
padding: 4px 10px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user