MelissaLayout: extrai Settings/Hero/Timeline + composables wallpaper/toques + push-back veil perf
- MelissaSettingsPanel.vue: painel Personalizar (Plano de Fundo, Relogio & Som, Tema com preset Lara/Nora) - MelissaHeroClock.vue: relogio gigante + saudacao + cronometro + resumo do dia - MelissaTimelineHoje.vue: timeline horizontal (lg+) e vertical (mobile) com eco/cursor agora - useMelissaWallpaper(): bgUrl/overlayOpacity/bgImageOpacity + onFileChange/clearBg + photoStyle/defaultBgStyle - useMelissaToques(): toqueTermino + testarToque (preferencia, nao instance state do cronometro) - Push-back perf: filter:blur animado no .win11-summary substituido por veil unico com backdrop-filter (1 backdrop pass por frame em vez de N glass-panels re-blurados; will-change + contain:strict + transform/opacity GPU-friendly; 60fps em mobile) MelissaLayout: 4114 -> 2861 linhas (-1253, -30%) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* useMelissaToques — preferencia de toque de termino do Melissa
|
||||
* -------------------------------------------------------------
|
||||
* Encapsula apenas a preferencia (qual toque tocar) e o botao de
|
||||
* teste do painel Personalizar. NAO controla o cronometro em si —
|
||||
* o componente <MelissaCronometro> recebe `toque-termino` como prop
|
||||
* e dispara o som ao final da sessao com a propria logica.
|
||||
*
|
||||
* Estado:
|
||||
* - toqueTermino: string — id do toque selecionado (default 'sino')
|
||||
*
|
||||
* Acao:
|
||||
* - testarToque(): toca o toque selecionado (preview no Personalizar)
|
||||
*
|
||||
* Constante exportada:
|
||||
* - TOQUE_IDS: Set<string> — ids validos, usado pra sanitizar payload
|
||||
* vindo de localStorage/DB no MelissaLayout
|
||||
*
|
||||
* Persistencia: NAO eh responsabilidade deste composable. O pai
|
||||
* (MelissaLayout) persiste `toqueTermino` junto com outras prefs em
|
||||
* user_settings.melissa_prefs.
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
import { TOQUES, playToque } from '../melissaToques';
|
||||
|
||||
export const TOQUE_IDS = new Set(TOQUES.map((t) => t.id));
|
||||
|
||||
export function useMelissaToques(initialId = 'sino') {
|
||||
// Sanitiza o default — se passarem id invalido cai pro 'sino'
|
||||
const safeInitial = TOQUE_IDS.has(initialId) ? initialId : 'sino';
|
||||
const toqueTermino = ref(safeInitial);
|
||||
|
||||
function testarToque() {
|
||||
playToque(toqueTermino.value);
|
||||
}
|
||||
|
||||
return {
|
||||
toqueTermino,
|
||||
testarToque
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* useMelissaWallpaper — wallpaper/background do MelissaLayout
|
||||
* -----------------------------------------------------------
|
||||
* Encapsula o estado e as operacoes do plano de fundo:
|
||||
* - bgUrl: data URL da imagem custom (vazio = usa gradiente default)
|
||||
* - overlayOpacity: 0–0.8, escurecedor sobre o bg (sempre aplicado)
|
||||
* - bgImageOpacity: 0.01–1, transparencia da foto custom (so quando bgUrl)
|
||||
*
|
||||
* Operacoes:
|
||||
* - onFileChange(e): valida tipo + tamanho, gera data URL
|
||||
* - clearBg(): zera bgUrl pra voltar ao gradiente default
|
||||
*
|
||||
* Estilos prontos:
|
||||
* - defaultBgStyle: gradiente bloom radial + linear, sempre renderizado
|
||||
* atras de tudo (cores via CSS vars que flipam com dark/light)
|
||||
* - photoStyle: computed que liga url(bgUrl) + opacity(bgImageOpacity)
|
||||
*
|
||||
* Persistencia: NAO eh responsabilidade deste composable. O pai
|
||||
* (MelissaLayout) persiste estes refs junto com outras prefs em
|
||||
* localStorage + user_settings.melissa_prefs.
|
||||
*/
|
||||
import { ref, computed } from 'vue';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
|
||||
// Limite de upload — protege quota do localStorage (~5MB) e evita data URL
|
||||
// gigante atravessando a UI. JPG/PNG 1920×1080 cabe folgado nesse teto.
|
||||
export const MAX_BG_BYTES = 2 * 1024 * 1024; // 2 MB
|
||||
|
||||
// Gradiente default — sempre renderizado no .win11-root (atras de tudo).
|
||||
// Quando o user faz upload, .win11-photo aparece por cima com opacidade
|
||||
// controlada pelo slider — permite blend natural com o gradiente abaixo.
|
||||
// Cores vem de CSS vars que flipam com dark/light AND seguem o preset
|
||||
// (ver style global no MelissaLayout: --bloom-c1/c2/base-1/base-2).
|
||||
export const defaultBgStyle = Object.freeze({
|
||||
backgroundImage:
|
||||
'radial-gradient(circle at 70% 30%, var(--bloom-c1) 0%, transparent 55%), radial-gradient(circle at 25% 75%, var(--bloom-c2) 0%, transparent 50%), linear-gradient(135deg, var(--bloom-base-1) 0%, var(--bloom-base-2) 50%, var(--bloom-base-1) 100%)',
|
||||
backgroundSize: 'cover'
|
||||
});
|
||||
|
||||
export function useMelissaWallpaper() {
|
||||
const toast = useToast();
|
||||
|
||||
const bgUrl = ref(''); // vazio = usa gradiente default
|
||||
const overlayOpacity = ref(0.35); // 0–0.8 — escurecedor sobre o bg
|
||||
const bgImageOpacity = ref(1); // 0.01–1 — transparencia da foto custom
|
||||
|
||||
function onFileChange(e) {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
if (!file.type.startsWith('image/')) {
|
||||
toast.add({
|
||||
severity: 'warn',
|
||||
summary: 'Formato inválido',
|
||||
detail: 'Selecione um arquivo de imagem (JPG, PNG, WEBP).',
|
||||
life: 4000
|
||||
});
|
||||
e.target.value = '';
|
||||
return;
|
||||
}
|
||||
if (file.size > MAX_BG_BYTES) {
|
||||
toast.add({
|
||||
severity: 'warn',
|
||||
summary: 'Imagem muito grande',
|
||||
detail: 'Máximo 2 MB. Reduza a resolução ou compressão e tente novamente.',
|
||||
life: 4500
|
||||
});
|
||||
e.target.value = '';
|
||||
return;
|
||||
}
|
||||
const reader = new FileReader();
|
||||
reader.onload = (ev) => (bgUrl.value = ev.target.result);
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function clearBg() {
|
||||
bgUrl.value = '';
|
||||
}
|
||||
|
||||
const photoStyle = computed(() => ({
|
||||
backgroundImage: bgUrl.value ? `url(${bgUrl.value})` : 'none',
|
||||
opacity: bgImageOpacity.value
|
||||
}));
|
||||
|
||||
return {
|
||||
bgUrl,
|
||||
overlayOpacity,
|
||||
bgImageOpacity,
|
||||
MAX_BG_BYTES,
|
||||
defaultBgStyle,
|
||||
photoStyle,
|
||||
onFileChange,
|
||||
clearBg
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user