This commit is contained in:
2026-09-16 13:36:46 +02:00
parent fdb684e96f
commit 93914ad02a
2 changed files with 604 additions and 176 deletions
+39 -13
View File
@@ -41,11 +41,12 @@
--badge-tag: #090909; --badge-tag: #090909;
--posts-meta-bg: #000000b8; --posts-meta-bg: #000000b8;
--x2154: #215483; --x2154: #215483;
--bg-canvas-opacity: 1.0; --bg-canvas-opacity: 0.81;
--bg-canvas-blur: 30px; --bg-canvas-blur: 151px;
--bg-canvas-color: transparent; --bg-canvas-zoom: 1.38;
--bg-canvas-color-opacity: 0.0; --bg-canvas-color: rgba(0, 0, 0, 0.09);
--onara-bg-opacity: 0.7; --bg-canvas-color-opacity: 0.09;
--onara-bg-opacity: 0.4;
--onara-backdrop-blur: 5px; --onara-backdrop-blur: 5px;
--onara-grid-dim-opacity: 0.38; --onara-grid-dim-opacity: 0.38;
} }
@@ -10391,11 +10392,11 @@ div.favs div.posts {
bottom: 0; bottom: 0;
width: calc(100vw - var(--sidebar-w, 300px)); width: calc(100vw - var(--sidebar-w, 300px));
height: 100%; height: 100%;
transform: translate3d(0, 0, 0) scale(var(--bg-canvas-zoom, 1.12)); transform: translate3d(0, 0, 0) scale(var(--bg-canvas-zoom, 1.38));
transform-origin: center center; transform-origin: center center;
z-index: -2; z-index: -2;
transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1), width 0.3s ease-in-out; transition: opacity 1.5s cubic-bezier(0.4, 0, 0.2, 1), width 0.3s ease-in-out;
opacity: var(--bg-canvas-opacity, 1.0); opacity: var(--bg-canvas-opacity, 0.81);
filter: var(--bg-canvas-filter, none); filter: var(--bg-canvas-filter, none);
-webkit-filter: var(--bg-canvas-filter, none); -webkit-filter: var(--bg-canvas-filter, none);
background-color: var(--bg-canvas-color, transparent); background-color: var(--bg-canvas-color, transparent);
@@ -20947,7 +20948,7 @@ body.onara-modal-open .admin-bar.open {
z-index: 10050 !important; z-index: 10050 !important;
display: none; display: none;
flex-direction: column !important; flex-direction: column !important;
background: rgba(0, 0, 0, var(--onara-bg-opacity, 0.7)) !important; background: rgba(0, 0, 0, var(--onara-bg-opacity, 0.4)) !important;
backdrop-filter: blur(var(--onara-backdrop-blur, 5px)) saturate(130%) !important; backdrop-filter: blur(var(--onara-backdrop-blur, 5px)) saturate(130%) !important;
-webkit-backdrop-filter: blur(var(--onara-backdrop-blur, 5px)) saturate(130%) !important; -webkit-backdrop-filter: blur(var(--onara-backdrop-blur, 5px)) saturate(130%) !important;
overflow-y: auto !important; overflow-y: auto !important;
@@ -21148,11 +21149,36 @@ body.layout-modern #onara-modal .item-layout-container .item-main-content {
margin-right: auto !important; margin-right: auto !important;
} }
#onara-modal .content:not(.slide-next):not(.slide-prev):not(.slide-out-left):not(.slide-out-right):not(.fade-in-zoom):not(.fade-out-zoom), #onara-modal .content.slide-next,
#onara-modal ._204863:not(.slide-next):not(.slide-prev):not(.slide-out-left):not(.slide-out-right):not(.fade-in-zoom):not(.fade-out-zoom), #onara-modal .content.slide-prev,
#onara-modal .item_title:not(.slide-next):not(.slide-prev):not(.slide-out-left):not(.slide-out-right):not(.fade-in-zoom):not(.fade-out-zoom), #onara-modal ._204863.slide-next,
#onara-modal .item-title:not(.slide-next):not(.slide-prev):not(.slide-out-left):not(.slide-out-right):not(.fade-in-zoom):not(.fade-out-zoom) { #onara-modal ._204863.slide-prev,
animation: contentSlideIn 0.55s cubic-bezier(0.16, 1, 0.3, 1) both !important; #onara-modal .item_title.slide-next,
#onara-modal .item_title.slide-prev,
#onara-modal .item-title.slide-next,
#onara-modal .item-title.slide-prev,
#onara-modal .content:not(.slide-out-left):not(.slide-out-right):not(.fade-out-zoom),
#onara-modal ._204863:not(.slide-out-left):not(.slide-out-right):not(.fade-out-zoom),
#onara-modal .item_title:not(.slide-out-left):not(.slide-out-right):not(.fade-out-zoom),
#onara-modal .item-title:not(.slide-out-left):not(.slide-out-right):not(.fade-out-zoom) {
animation: contentFadeInZoom 0.32s cubic-bezier(0.16, 1, 0.3, 1) both !important;
will-change: transform, opacity;
}
#onara-modal .content.slide-out-left,
#onara-modal .content.slide-out-right,
#onara-modal ._204863.slide-out-left,
#onara-modal ._204863.slide-out-right,
#onara-modal .item_title.slide-out-left,
#onara-modal .item_title.slide-out-right,
#onara-modal .item-title.slide-out-left,
#onara-modal .item-title.slide-out-right,
#onara-modal .content.fade-out-zoom,
#onara-modal ._204863.fade-out-zoom,
#onara-modal .item_title.fade-out-zoom,
#onara-modal .item-title.fade-out-zoom {
animation: contentFadeOutZoom 0.18s cubic-bezier(0.3, 0, 0.8, 1) forwards !important;
pointer-events: none;
will-change: transform, opacity; will-change: transform, opacity;
} }
+518 -116
View File
@@ -1682,7 +1682,11 @@ window.cancelAnimFrame = (function () {
modal._closeTimeout = null; modal._closeTimeout = null;
} }
modal._isClosing = false; modal._isClosing = false;
const wasAlreadyOpen = document.body.classList.contains('onara-modal-open');
document.body.classList.add('onara-modal-open'); document.body.classList.add('onara-modal-open');
if (typeof window.applyBackgroundOpacitySettings === 'function') {
window.applyBackgroundOpacitySettings(null, true);
}
if (document.querySelector('.index-container') && window.scrollY !== 0) { if (document.querySelector('.index-container') && window.scrollY !== 0) {
window.scrollTo(0, 0); window.scrollTo(0, 0);
} }
@@ -1698,9 +1702,9 @@ window.cancelAnimFrame = (function () {
canvas.classList.remove('fader-out', 'fast-fade'); canvas.classList.remove('fader-out', 'fast-fade');
canvas.classList.add('fader-in'); canvas.classList.add('fader-in');
canvas.style.transition = 'none'; canvas.style.transition = 'none';
canvas.style.opacity = 'var(--bg-canvas-opacity, 1.0)'; canvas.style.opacity = 'var(--bg-canvas-opacity, 0.95)';
} }
if (typeof window.paintImmediateBgThumb === 'function') { if (!wasAlreadyOpen && typeof window.paintImmediateBgThumb === 'function') {
window.paintImmediateBgThumb(); window.paintImmediateBgThumb();
} }
@@ -1978,6 +1982,9 @@ window.cancelAnimFrame = (function () {
modal.classList.remove('onara-fade-in'); modal.classList.remove('onara-fade-in');
modal.classList.add('onara-fade-out'); modal.classList.add('onara-fade-out');
document.body.classList.remove('onara-modal-open'); document.body.classList.remove('onara-modal-open');
if (typeof window.applyBackgroundOpacitySettings === 'function') {
window.applyBackgroundOpacitySettings(null, false);
}
// Destroy background instance when Onara modal closes so it doesn't linger on the index grid // Destroy background instance when Onara modal closes so it doesn't linger on the index grid
if (window.destroyBackgroundInstance) { if (window.destroyBackgroundInstance) {
@@ -4028,6 +4035,199 @@ window.cancelAnimFrame = (function () {
} }
}; };
let mediaAmbientSampleCanvas = null;
let mediaAmbientSampleCtx = null;
const sampleMediaSpatialColors = (source) => {
if (!source) return null;
const w = source.videoWidth || source.naturalWidth || source.width || 0;
const h = source.videoHeight || source.naturalHeight || source.height || 0;
if (w <= 0 || h <= 0) return null;
if (!mediaAmbientSampleCanvas) {
mediaAmbientSampleCanvas = document.createElement('canvas');
mediaAmbientSampleCanvas.width = 4;
mediaAmbientSampleCanvas.height = 4;
mediaAmbientSampleCtx = mediaAmbientSampleCanvas.getContext('2d', { willReadFrequently: true });
}
if (!mediaAmbientSampleCtx) return null;
try {
mediaAmbientSampleCtx.clearRect(0, 0, 4, 4);
mediaAmbientSampleCtx.drawImage(source, 0, 0, 4, 4);
const data = mediaAmbientSampleCtx.getImageData(0, 0, 4, 4).data;
// Sample top half (rows 0, 1) and bottom half (rows 2, 3)
let rTop = 0, gTop = 0, bTop = 0, countTop = 0;
let rBot = 0, gBot = 0, bBot = 0, countBot = 0;
for (let y = 0; y < 4; y++) {
for (let x = 0; x < 4; x++) {
const idx = (y * 4 + x) * 4;
const a = data[idx + 3];
if (a > 20) {
if (y < 2) {
rTop += data[idx];
gTop += data[idx + 1];
bTop += data[idx + 2];
countTop++;
} else {
rBot += data[idx];
gBot += data[idx + 1];
bBot += data[idx + 2];
countBot++;
}
}
}
}
const top = countTop > 0
? [Math.round(rTop / countTop), Math.round(gTop / countTop), Math.round(bTop / countTop)]
: [25, 25, 35];
const bot = countBot > 0
? [Math.round(rBot / countBot), Math.round(gBot / countBot), Math.round(bBot / countBot)]
: [25, 25, 35];
return { top, bottom: bot };
} catch (e) {
return null;
}
};
const boostColorSaturation = (rgb, boost = 1.35) => {
if (!rgb || !Array.isArray(rgb) || rgb.length < 3) return [30, 30, 40];
const [r, g, b] = rgb;
const lum = 0.299 * r + 0.587 * g + 0.114 * b;
return [
Math.min(255, Math.max(0, Math.round(lum + (r - lum) * boost))),
Math.min(255, Math.max(0, Math.round(lum + (g - lum) * boost))),
Math.min(255, Math.max(0, Math.round(lum + (b - lum) * boost)))
];
};
const mediaAmbientState = {
top: [25, 25, 35],
targetTop: [25, 25, 35],
bottom: [25, 25, 35],
targetBottom: [25, 25, 35],
hasSampled: false
};
const setAmbientTargetColors = (colors) => {
if (!colors) return;
if (colors.top) mediaAmbientState.targetTop = [...colors.top];
if (colors.bottom) mediaAmbientState.targetBottom = [...colors.bottom];
};
const updateAmbientLerp = (smoothingFactor) => {
const cfg = (typeof window.getEffectiveBackgroundTuning === 'function' ? window.getEffectiveBackgroundTuning() : null) || window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING;
const factor = smoothingFactor !== undefined
? smoothingFactor
: (cfg.bgAmbientSmoothness !== undefined ? Number(cfg.bgAmbientSmoothness) : 0.05);
const s = mediaAmbientState;
for (let i = 0; i < 3; i++) {
s.top[i] += (s.targetTop[i] - s.top[i]) * factor;
s.bottom[i] += (s.targetBottom[i] - s.bottom[i]) * factor;
}
return {
top: s.top.map(v => Math.min(255, Math.max(0, Math.round(v)))),
bottom: s.bottom.map(v => Math.min(255, Math.max(0, Math.round(v))))
};
};
const renderAmbientLightingWash = (ctx, targetW, targetH, colors, alpha = 1.0) => {
if (!ctx || !colors) return;
const cfg = (typeof window.getEffectiveBackgroundTuning === 'function' ? window.getEffectiveBackgroundTuning() : null) || window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING;
const satBoost = cfg.bgAmbientSaturation !== undefined ? Number(cfg.bgAmbientSaturation) : 1.35;
const intensity = cfg.bgAmbientIntensity !== undefined ? Number(cfg.bgAmbientIntensity) : 1.0;
const spread = cfg.bgAmbientSpread !== undefined ? Number(cfg.bgAmbientSpread) : 0.85;
const rawTop = colors.top || colors;
const rawBot = colors.bottom || colors.top || colors;
const top = boostColorSaturation(rawTop, satBoost);
const bot = boostColorSaturation(rawBot, satBoost);
ctx.save();
if (alpha < 1.0) ctx.globalAlpha = alpha;
const cx = targetW / 2;
const cyTop = targetH * 0.28;
const cyBot = targetH * 0.72;
const maxR = Math.max(targetW, targetH) * spread;
// 1. Dark background base
ctx.fillStyle = '#06060c';
ctx.fillRect(0, 0, targetW, targetH);
const a0 = Math.min(1.0, Math.max(0.0, 0.90 * intensity)).toFixed(3);
const a1 = Math.min(1.0, Math.max(0.0, 0.55 * intensity)).toFixed(3);
const a2 = Math.min(1.0, Math.max(0.0, 0.18 * intensity)).toFixed(3);
// 2. Top ambient lobe
const [rt, gt, bt] = top;
const gradTop = ctx.createRadialGradient(cx, cyTop, 0, cx, cyTop, maxR);
gradTop.addColorStop(0, `rgba(${rt}, ${gt}, ${bt}, ${a0})`);
gradTop.addColorStop(0.35, `rgba(${Math.round(rt * 0.75)}, ${Math.round(gt * 0.75)}, ${Math.round(bt * 0.75)}, ${a1})`);
gradTop.addColorStop(0.70, `rgba(${Math.round(rt * 0.35)}, ${Math.round(gt * 0.35)}, ${Math.round(bt * 0.35)}, ${a2})`);
gradTop.addColorStop(1.0, 'rgba(6, 6, 12, 0.0)');
ctx.fillStyle = gradTop;
ctx.fillRect(0, 0, targetW, targetH);
// 3. Bottom ambient lobe
const [rb, gb, bb] = bot;
const gradBot = ctx.createRadialGradient(cx, cyBot, 0, cx, cyBot, maxR);
gradBot.addColorStop(0, `rgba(${rb}, ${gb}, ${bb}, ${a0})`);
gradBot.addColorStop(0.35, `rgba(${Math.round(rb * 0.75)}, ${Math.round(gb * 0.75)}, ${Math.round(bb * 0.75)}, ${a1})`);
gradBot.addColorStop(0.70, `rgba(${Math.round(rb * 0.35)}, ${Math.round(gb * 0.35)}, ${Math.round(bb * 0.35)}, ${a2})`);
gradBot.addColorStop(1.0, 'rgba(6, 6, 12, 0.0)');
ctx.fillStyle = gradBot;
ctx.fillRect(0, 0, targetW, targetH);
ctx.restore();
};
let ambientTransitionRaf = null;
const transitionAmbientToColors = (colors, onComplete) => {
setAmbientTargetColors(colors);
if (ambientTransitionRaf) window.cancelAnimFrame(ambientTransitionRaf);
const canvas = document.getElementById('bg');
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
let frames = 0;
const maxFrames = 30; // ~500ms smooth cinematic color melt
const step = () => {
const current = updateAmbientLerp(0.10);
const w = canvas.width;
const h = canvas.height;
ctx.clearRect(0, 0, w, h);
renderAmbientLightingWash(ctx, w, h, current, 1.0);
if (typeof window.applyBgColorOverlay === 'function') {
window.applyBgColorOverlay(ctx, w, h);
}
frames++;
const distTop = Math.abs(mediaAmbientState.top[0] - mediaAmbientState.targetTop[0]) +
Math.abs(mediaAmbientState.top[1] - mediaAmbientState.targetTop[1]) +
Math.abs(mediaAmbientState.top[2] - mediaAmbientState.targetTop[2]);
const distBot = Math.abs(mediaAmbientState.bottom[0] - mediaAmbientState.targetBottom[0]) +
Math.abs(mediaAmbientState.bottom[1] - mediaAmbientState.targetBottom[1]) +
Math.abs(mediaAmbientState.bottom[2] - mediaAmbientState.targetBottom[2]);
if (frames < maxFrames && (distTop > 2 || distBot > 2)) {
ambientTransitionRaf = window.requestAnimFrame(step);
} else {
ambientTransitionRaf = null;
if (onComplete) onComplete();
}
};
ambientTransitionRaf = window.requestAnimFrame(step);
};
// Paint background canvas immediately from thumbnail without network or animation delay // Paint background canvas immediately from thumbnail without network or animation delay
window.paintImmediateBgThumb = (thumbOrElem) => { window.paintImmediateBgThumb = (thumbOrElem) => {
if (typeof background !== 'undefined' && !background) return; if (typeof background !== 'undefined' && !background) return;
@@ -4058,18 +4258,18 @@ window.cancelAnimFrame = (function () {
canvas.classList.remove('fader-out', 'fast-fade'); canvas.classList.remove('fader-out', 'fast-fade');
canvas.classList.add('fader-in'); canvas.classList.add('fader-in');
canvas.style.transition = 'none'; canvas.style.transition = 'none';
canvas.style.opacity = 'var(--bg-canvas-opacity, 1.0)'; canvas.style.opacity = document.body.classList.contains('onara-modal-open') ? 'var(--bg-canvas-opacity, 0.95)' : 'var(--bg-canvas-opacity, 0.81)';
const ctx = canvas.getContext('2d'); const ctx = canvas.getContext('2d');
if (!ctx) return; if (!ctx) return;
const cfg = (typeof window.audioVisualizerTuning !== 'undefined' && window.audioVisualizerTuning) || (typeof DEFAULT_AUDIO_TUNING !== 'undefined' ? DEFAULT_AUDIO_TUNING : {}); const cfg = (typeof window.getEffectiveBackgroundTuning === 'function' ? window.getEffectiveBackgroundTuning(true) : null) || (typeof window.audioVisualizerTuning !== 'undefined' && window.audioVisualizerTuning) || (typeof DEFAULT_AUDIO_TUNING !== 'undefined' ? DEFAULT_AUDIO_TUNING : {});
const method = cfg.bgBlurMethod !== undefined ? Number(cfg.bgBlurMethod) : 0; const method = cfg.bgBlurMethod !== undefined ? Number(cfg.bgBlurMethod) : 0;
const bSetting = cfg.bgCanvasBlur !== undefined ? Number(cfg.bgCanvasBlur) : 30; const bSetting = cfg.bgCanvasBlur !== undefined ? Number(cfg.bgCanvasBlur) : 31;
const bPx = method === 1 ? 0 : Math.round(bSetting * SCALE); const bPx = (method === 1 || method === 2) ? 0 : Math.round(bSetting * SCALE);
const bVal = cfg.bgCanvasBrightness !== undefined ? Number(cfg.bgCanvasBrightness) : 0.45; const bVal = cfg.bgCanvasBrightness !== undefined ? Number(cfg.bgCanvasBrightness) : 0.6;
const sat = cfg.bgCanvasSaturate !== undefined ? Number(cfg.bgCanvasSaturate) : 1.8; const sat = cfg.bgCanvasSaturate !== undefined ? Number(cfg.bgCanvasSaturate) : 1.85;
const con = cfg.bgCanvasContrast !== undefined ? Number(cfg.bgCanvasContrast) : 1.1; const con = cfg.bgCanvasContrast !== undefined ? Number(cfg.bgCanvasContrast) : 1.15;
const parts = []; const parts = [];
if (bPx > 0) parts.push(`blur(${bPx}px)`); if (bPx > 0) parts.push(`blur(${bPx}px)`);
if (bVal !== 1) parts.push(`brightness(${bVal})`); if (bVal !== 1) parts.push(`brightness(${bVal})`);
@@ -4077,13 +4277,22 @@ window.cancelAnimFrame = (function () {
if (con !== 1) parts.push(`contrast(${con})`); if (con !== 1) parts.push(`contrast(${con})`);
const filterStr = parts.length > 0 ? parts.join(' ') : 'none'; const filterStr = parts.length > 0 ? parts.join(' ') : 'none';
const useAmbient = Number(cfg.bgAmbientLighting) === 1;
if (useAmbient) {
ctx.clearRect(0, 0, cw, ch);
const sampled = sampleMediaSpatialColors(img) || { top: [30, 30, 45], bottom: [30, 30, 45] };
setAmbientTargetColors(sampled);
mediaAmbientState.top = [...mediaAmbientState.targetTop];
mediaAmbientState.bottom = [...mediaAmbientState.targetBottom];
renderAmbientLightingWash(ctx, cw, ch, sampled, 1.0);
} else {
ctx.clearRect(0, 0, cw, ch); ctx.clearRect(0, 0, cw, ch);
ctx.save(); ctx.save();
ctx.filter = filterStr; ctx.filter = filterStr;
let sw = img.naturalWidth || img.width; let sw = img.naturalWidth || img.width;
let sh = img.naturalHeight || img.height; let sh = img.naturalHeight || img.height;
if (sw > 0 && sh > 0) { if (sw > 0 && sw > 0) {
const scale = Math.max(cw / sw, ch / sh); const scale = Math.max(cw / sw, ch / sh);
const dw = sw * scale; const dw = sw * scale;
const dh = sh * scale; const dh = sh * scale;
@@ -4094,6 +4303,7 @@ window.cancelAnimFrame = (function () {
ctx.drawImage(img, 0, 0, cw, ch); ctx.drawImage(img, 0, 0, cw, ch);
} }
ctx.restore(); ctx.restore();
}
// Color overlay tint if configured // Color overlay tint if configured
const colOp = cfg.bgCanvasColorOpacity !== undefined ? Math.min(1, Math.max(0, Number(cfg.bgCanvasColorOpacity))) : 0; const colOp = cfg.bgCanvasColorOpacity !== undefined ? Math.min(1, Math.max(0, Number(cfg.bgCanvasColorOpacity))) : 0;
@@ -4216,20 +4426,22 @@ window.cancelAnimFrame = (function () {
} }
const context = canvas.getContext('2d'); const context = canvas.getContext('2d');
const cfgCurrent = window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING; const cfgCurrent = (typeof window.getEffectiveBackgroundTuning === 'function' ? window.getEffectiveBackgroundTuning() : null) || window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING;
const blurSetting = cfgCurrent.bgCanvasBlur !== undefined ? Number(cfgCurrent.bgCanvasBlur) : 30; const blurSetting = cfgCurrent.bgCanvasBlur !== undefined ? Number(cfgCurrent.bgCanvasBlur) : 31;
const blurPx = Math.round(blurSetting * SCALE); const methodSetting = cfgCurrent.bgBlurMethod !== undefined ? Number(cfgCurrent.bgBlurMethod) : 0;
const brightnessVal = cfgCurrent.bgCanvasBrightness !== undefined ? Number(cfgCurrent.bgCanvasBrightness) : 0.45; const blurPx = (methodSetting === 1 || methodSetting === 2) ? 0 : Math.round(blurSetting * SCALE);
const brightnessVal = cfgCurrent.bgCanvasBrightness !== undefined ? Number(cfgCurrent.bgCanvasBrightness) : 0.6;
const getCanvasFilter = () => { const getCanvasFilter = () => {
const cfg = window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING; const cfg = (typeof window.getEffectiveBackgroundTuning === 'function' ? window.getEffectiveBackgroundTuning() : null) || window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING;
const method = cfg.bgBlurMethod !== undefined ? Number(cfg.bgBlurMethod) : 0; const method = cfg.bgBlurMethod !== undefined ? Number(cfg.bgBlurMethod) : 0;
const bSetting = cfg.bgCanvasBlur !== undefined ? Number(cfg.bgCanvasBlur) : 30; const bSetting = cfg.bgCanvasBlur !== undefined ? Number(cfg.bgCanvasBlur) : 31;
// Method 1 = GPU CSS filter only (no canvas blur, handled by CSS --bg-canvas-filter) // Method 1 = GPU CSS filter only (no canvas blur, handled by CSS --bg-canvas-filter)
const bPx = method === 1 ? 0 : Math.round(bSetting * SCALE); // Method 2 = Hybrid (CSS blur + canvas sat/contrast)
const bVal = cfg.bgCanvasBrightness !== undefined ? Number(cfg.bgCanvasBrightness) : 0.45; const bPx = (method === 1 || method === 2) ? 0 : Math.round(bSetting * SCALE);
const sat = cfg.bgCanvasSaturate !== undefined ? Number(cfg.bgCanvasSaturate) : 1.8; const bVal = cfg.bgCanvasBrightness !== undefined ? Number(cfg.bgCanvasBrightness) : 0.6;
const con = cfg.bgCanvasContrast !== undefined ? Number(cfg.bgCanvasContrast) : 1.1; const sat = cfg.bgCanvasSaturate !== undefined ? Number(cfg.bgCanvasSaturate) : 1.85;
const con = cfg.bgCanvasContrast !== undefined ? Number(cfg.bgCanvasContrast) : 1.15;
const parts = []; const parts = [];
if (bPx > 0) parts.push(`blur(${bPx}px)`); if (bPx > 0) parts.push(`blur(${bPx}px)`);
if (bVal !== 1) parts.push(`brightness(${bVal})`); if (bVal !== 1) parts.push(`brightness(${bVal})`);
@@ -4270,7 +4482,7 @@ window.cancelAnimFrame = (function () {
}; };
const applyBgColorOverlay = (ctx, w, h) => { const applyBgColorOverlay = (ctx, w, h) => {
const cfg = window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING; const cfg = (typeof window.getEffectiveBackgroundTuning === 'function' ? window.getEffectiveBackgroundTuning() : null) || window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING;
const colOp = cfg.bgCanvasColorOpacity !== undefined ? Math.min(1, Math.max(0, Number(cfg.bgCanvasColorOpacity))) : 0; const colOp = cfg.bgCanvasColorOpacity !== undefined ? Math.min(1, Math.max(0, Number(cfg.bgCanvasColorOpacity))) : 0;
if (colOp > 0.001) { if (colOp > 0.001) {
const col = cfg.bgCanvasColor || '#000000'; const col = cfg.bgCanvasColor || '#000000';
@@ -4287,8 +4499,15 @@ window.cancelAnimFrame = (function () {
syncCanvasDimensions(); syncCanvasDimensions();
const targetW = canvas.width; const targetW = canvas.width;
const targetH = canvas.height; const targetH = canvas.height;
const isOnara = document.body.classList.contains('onara-modal-open') || (typeof isOnaraActive === 'function' && isOnaraActive()); const useAmbient = Number(cfgCurrent.bgAmbientLighting) === 1;
if (!prevSnapshot || isOnara) {
if (useAmbient) {
const sampled = sampleMediaSpatialColors(sourceImg) || { top: [30, 30, 45], bottom: [30, 30, 45] };
transitionAmbientToColors(sampled, onComplete);
return;
}
if (!prevSnapshot) {
context.clearRect(0, 0, targetW, targetH); context.clearRect(0, 0, targetW, targetH);
context.save(); context.save();
context.filter = getCanvasFilter(); context.filter = getCanvasFilter();
@@ -4312,15 +4531,12 @@ window.cancelAnimFrame = (function () {
const h = canvas.height; const h = canvas.height;
context.clearRect(0, 0, w, h); context.clearRect(0, 0, w, h);
// 1. Fade out previous background // 1. Base: keep previous background at full opacity underneath (prevents transparent dip)
if (ease < 1.0 && prevSnapshot) { if (prevSnapshot) {
context.save();
context.globalAlpha = 1.0 - ease;
context.drawImage(prevSnapshot, 0, 0, w, h); context.drawImage(prevSnapshot, 0, 0, w, h);
context.restore();
} }
// 2. Fade in new background with blur and brightness // 2. Fade in new background smoothly over previous
context.save(); context.save();
context.globalAlpha = ease; context.globalAlpha = ease;
context.filter = getCanvasFilter(); context.filter = getCanvasFilter();
@@ -4352,35 +4568,36 @@ window.cancelAnimFrame = (function () {
if (itemId) { if (itemId) {
if (document.body.classList.contains('onara-modal-open')) { if (document.body.classList.contains('onara-modal-open')) {
const onaraThumbImg = document.querySelector('.posts > a.thumb.onara-active img, .posts > a.thumb:focus img'); const onaraThumbImg = document.querySelector('.posts > a.thumb.onara-active img, .posts > a.thumb:focus img');
if (onaraThumbImg && onaraThumbImg.complete && onaraThumbImg.naturalWidth > 0) {
crossfadeToSource(onaraThumbImg, () => {
if (isDrawable && elem.complete && elem.naturalWidth > 0) { if (isDrawable && elem.complete && elem.naturalWidth > 0) {
crossfadeToSource(elem); crossfadeToSource(elem);
return;
} }
}); if (onaraThumbImg && onaraThumbImg.complete && onaraThumbImg.naturalWidth > 0) {
crossfadeToSource(onaraThumbImg);
return; return;
} }
} }
const thumb = new Image(); let resolved = false;
thumb.onload = () => { const onLoaded = (src) => {
crossfadeToSource(thumb, () => { if (resolved) return;
if (isDrawable) { resolved = true;
if (elem.complete && elem.naturalWidth > 0) { crossfadeToSource(src);
crossfadeToSource(elem);
} else {
elem.onload = () => crossfadeToSource(elem);
}
}
});
}; };
thumb.onerror = () => {
if (isDrawable) { if (isDrawable) {
if (elem.complete && elem.naturalWidth > 0) { if (elem.complete && elem.naturalWidth > 0) {
crossfadeToSource(elem); onLoaded(elem);
} else { return;
elem.onload = () => crossfadeToSource(elem);
} }
elem.onload = () => onLoaded(elem);
}
const thumb = new Image();
thumb.onload = () => onLoaded(thumb);
thumb.onerror = () => {
if (isDrawable && elem.complete && elem.naturalWidth > 0) {
onLoaded(elem);
} }
}; };
let newSrc = `/t/${itemId}.webp`; let newSrc = `/t/${itemId}.webp`;
@@ -4407,19 +4624,46 @@ window.cancelAnimFrame = (function () {
const curCw = canvas.width; const curCw = canvas.width;
const curCh = canvas.height; const curCh = canvas.height;
context.clearRect(0, 0, curCw, curCh); context.clearRect(0, 0, curCw, curCh);
const isOnara = document.body.classList.contains('onara-modal-open') || (typeof isOnaraActive === 'function' && isOnaraActive());
if (prevSnapshot && !isOnara) { const useAmbient = Number(cfgCurrent.bgAmbientLighting) === 1;
if (useAmbient) {
if (!elem._ambientFrameCount) elem._ambientFrameCount = 0;
elem._ambientFrameCount++;
if (elem._ambientFrameCount % 3 === 0 || !mediaAmbientState.hasSampled) {
const sampled = sampleMediaSpatialColors(elem);
if (sampled) {
setAmbientTargetColors(sampled);
mediaAmbientState.hasSampled = true;
}
}
// Smooth exponential lerp on every single RAF frame (0.05 per frame = buttery smooth)
const current = updateAmbientLerp(0.05);
if (prevSnapshot) {
const elapsed = performance.now() - videoFadeStartTime; const elapsed = performance.now() - videoFadeStartTime;
const progress = Math.min(1.0, elapsed / 350); const progress = Math.min(1.0, elapsed / 350);
const ease = progress < 0.5 ? 2 * progress * progress : -1 + (4 - 2 * progress) * progress; const ease = progress < 0.5 ? 2 * progress * progress : -1 + (4 - 2 * progress) * progress;
if (ease < 1.0) {
context.save();
context.globalAlpha = 1.0 - ease;
context.drawImage(prevSnapshot, 0, 0, curCw, curCh); context.drawImage(prevSnapshot, 0, 0, curCw, curCh);
context.restore(); renderAmbientLightingWash(context, curCw, curCh, current, ease);
}
if (progress >= 1.0) {
prevSnapshot = null;
}
} else {
renderAmbientLightingWash(context, curCw, curCh, current, 1.0);
}
} else {
if (prevSnapshot) {
const elapsed = performance.now() - videoFadeStartTime;
const progress = Math.min(1.0, elapsed / 350);
const ease = progress < 0.5 ? 2 * progress * progress : -1 + (4 - 2 * progress) * progress;
// Base: previous frame drawn underneath at full opacity
context.drawImage(prevSnapshot, 0, 0, curCw, curCh);
// Video dissolves smoothly on top
context.save(); context.save();
context.globalAlpha = ease; context.globalAlpha = ease;
context.filter = getCanvasFilter(); context.filter = getCanvasFilter();
@@ -4432,7 +4676,7 @@ window.cancelAnimFrame = (function () {
prevSnapshot = null; prevSnapshot = null;
} }
} else { } else {
// Video already loaded or resumed from pause (or instant in Onara): render directly at 100% opacity without fading from black // Video already loaded or resumed from pause: render directly at 100% opacity without fading from black
context.save(); context.save();
context.filter = getCanvasFilter(); context.filter = getCanvasFilter();
try { try {
@@ -4440,6 +4684,7 @@ window.cancelAnimFrame = (function () {
} catch (e) {} } catch (e) {}
context.restore(); context.restore();
} }
}
// Color overlay tint if configured // Color overlay tint if configured
applyBgColorOverlay(context, curCw, curCh); applyBgColorOverlay(context, curCw, curCh);
@@ -4458,8 +4703,19 @@ window.cancelAnimFrame = (function () {
if (prevSnapshot) videoFadeStartTime = performance.now(); if (prevSnapshot) videoFadeStartTime = performance.now();
if (elem.tagName === 'VIDEO') { if (elem.tagName === 'VIDEO') {
// For album videos (reused element), remove old listeners first if (background) {
if (!elem.paused) {
if (bgRafId) window.cancelAnimFrame(bgRafId);
animationLoop();
}
// For album videos that haven't started yet, or Onara videos pending play, drawOnce with the thumbnail
else if (isAlbumVideo || document.body.classList.contains('onara-modal-open')) {
drawOnce();
}
}
const startVideoLoop = () => { const startVideoLoop = () => {
if (crossfadeRafId) window.cancelAnimFrame(crossfadeRafId);
if (bgRafId) window.cancelAnimFrame(bgRafId); if (bgRafId) window.cancelAnimFrame(bgRafId);
// Only reset fade start time if a previous snapshot is actively waiting to be crossfaded // Only reset fade start time if a previous snapshot is actively waiting to be crossfaded
if (prevSnapshot) videoFadeStartTime = performance.now(); if (prevSnapshot) videoFadeStartTime = performance.now();
@@ -4721,23 +4977,81 @@ window.cancelAnimFrame = (function () {
enableBlink: 0, enableBlink: 0,
blinkInterval: 12, blinkInterval: 12,
beatHueSpeed: 0.1, beatHueSpeed: 0.1,
onaraBgOpacity: 0.7, onaraBgOpacity: 0.4,
onaraBackdropBlur: 5, onaraBackdropBlur: 5,
bgCanvasColor: '#000000', bgCanvasColor: "#000000",
bgCanvasColorOpacity: 0, bgCanvasColorOpacity: 0.09,
bgCanvasOpacity: 1, bgCanvasOpacity: 0.81,
bgBlurMethod: 0, bgBlurMethod: 0,
bgCanvasBlur: 30, bgCanvasBlur: 151,
bgCanvasBrightness: 0.45, bgCanvasBrightness: 0.6,
bgCanvasSaturate: 1.8, bgCanvasSaturate: 1.9,
bgCanvasContrast: 1.1, bgCanvasContrast: 1.15,
bgCanvasZoom: 1.12, bgCanvasZoom: 1.38,
bgCanvasScale: 3, bgCanvasScale: 2.15,
onaraGridDim: 0.38, onaraGridDim: 0.38,
feedAudioToBg: 0 feedAudioToBg: 1,
bgAmbientLighting: 1,
bgAmbientIntensity: 1.9,
bgAmbientSpread: 0.5,
bgAmbientSaturation: 1.5,
bgAmbientSmoothness: 0.06
}; };
const TUNING_CONFIG_VERSION = '2026-09-16_tuner_v7'; const TUNING_CONFIG_VERSION = '2026-09-16_tuner_v12';
const DEFAULT_ONARA_TUNING = {
bgCanvasColor: "#000000",
bgCanvasColorOpacity: 0.8,
bgCanvasOpacity: 0.95,
bgBlurMethod: 2,
bgCanvasBlur: 31,
bgCanvasBrightness: 0.6,
bgCanvasSaturate: 1.85,
bgCanvasContrast: 1.15,
bgCanvasZoom: 1.38,
bgCanvasScale: 2.15,
feedAudioToBg: 1,
onaraBgOpacity: 0,
onaraBackdropBlur: 10,
onaraGridDim: 0,
bgAmbientLighting: 0,
bgAmbientIntensity: 1.0,
bgAmbientSpread: 0.85,
bgAmbientSaturation: 1.35,
bgAmbientSmoothness: 0.05
};
const ONARA_TUNING_CONFIG_VERSION = '2026-09-16_onara_v3';
let savedOnaraTuning = null;
try {
const appliedOnaraVer = localStorage.getItem('f0ck_onara_tuning_ver');
if (appliedOnaraVer !== ONARA_TUNING_CONFIG_VERSION) {
localStorage.setItem('f0ck_onara_tuning', JSON.stringify(DEFAULT_ONARA_TUNING));
localStorage.setItem('f0ck_onara_tuning_ver', ONARA_TUNING_CONFIG_VERSION);
savedOnaraTuning = Object.assign({}, DEFAULT_ONARA_TUNING);
} else {
const raw = localStorage.getItem('f0ck_onara_tuning');
if (raw) savedOnaraTuning = JSON.parse(raw);
}
} catch (e) {}
window.DEFAULT_ONARA_TUNING = DEFAULT_ONARA_TUNING;
window.onaraTuning = Object.assign({}, DEFAULT_ONARA_TUNING, savedOnaraTuning || {});
const isCurrentlyInOnara = () => {
return document.body.classList.contains('onara-modal-open') ||
(typeof isOnaraActive === 'function' && isOnaraActive() && !!document.getElementById('onara-item-mount'));
};
const getEffectiveBackgroundTuning = (forceMode) => {
const useOnara = forceMode !== undefined ? (forceMode === 'onara' || forceMode === true) : isCurrentlyInOnara();
if (useOnara) {
return Object.assign({}, window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING, window.onaraTuning || DEFAULT_ONARA_TUNING);
}
return window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING;
};
window.getEffectiveBackgroundTuning = getEffectiveBackgroundTuning;
const getCustomDefaultTuning = () => { const getCustomDefaultTuning = () => {
try { try {
@@ -4777,17 +5091,18 @@ window.cancelAnimFrame = (function () {
window.audioVisualizerTuning = Object.assign({}, getEffectiveDefaultTuning(), savedTuning || {}); window.audioVisualizerTuning = Object.assign({}, getEffectiveDefaultTuning(), savedTuning || {});
const applyBackgroundOpacitySettings = (cfg) => { const applyBackgroundOpacitySettings = (cfg, forceMode) => {
const c = cfg || window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING; const inOnara = forceMode !== undefined ? (forceMode === 'onara' || forceMode === true) : isCurrentlyInOnara();
const onaraOp = c.onaraBgOpacity !== undefined ? Math.min(1, Math.max(0, Number(c.onaraBgOpacity))) : 0.70; const c = cfg || getEffectiveBackgroundTuning(forceMode);
const bgOp = c.bgCanvasOpacity !== undefined ? Math.min(1, Math.max(0, Number(c.bgCanvasOpacity))) : 1.00; const onaraOp = c.onaraBgOpacity !== undefined ? Math.min(1, Math.max(0, Number(c.onaraBgOpacity))) : (inOnara ? 0.0 : 0.40);
const gridThumbOp = c.onaraGridDim !== undefined ? Math.min(1, Math.max(0, Number(c.onaraGridDim))) : 0.38; const bgOp = c.bgCanvasOpacity !== undefined ? Math.min(1, Math.max(0, Number(c.bgCanvasOpacity))) : (inOnara ? 0.95 : 0.81);
const onaraBlur = c.onaraBackdropBlur !== undefined ? Math.max(0, Number(c.onaraBackdropBlur)) : 5; const gridThumbOp = c.onaraGridDim !== undefined ? Math.min(1, Math.max(0, Number(c.onaraGridDim))) : (inOnara ? 0.0 : 0.38);
const bgBlur = c.bgCanvasBlur !== undefined ? Math.max(0, Number(c.bgCanvasBlur)) : 30; const onaraBlur = c.onaraBackdropBlur !== undefined ? Math.max(0, Number(c.onaraBackdropBlur)) : (inOnara ? 10 : 5);
const method = c.bgBlurMethod !== undefined ? Number(c.bgBlurMethod) : 0; const bgBlur = c.bgCanvasBlur !== undefined ? Math.max(0, Number(c.bgCanvasBlur)) : (inOnara ? 31 : 151);
const zoom = c.bgCanvasZoom !== undefined ? Math.max(1.0, Number(c.bgCanvasZoom)) : 1.12; const method = c.bgBlurMethod !== undefined ? Number(c.bgBlurMethod) : (inOnara ? 2 : 0);
const zoom = c.bgCanvasZoom !== undefined ? Math.max(1.0, Number(c.bgCanvasZoom)) : 1.38;
const bgColor = c.bgCanvasColor || '#000000'; const bgColor = c.bgCanvasColor || '#000000';
const bgColOp = c.bgCanvasColorOpacity !== undefined ? Math.min(1, Math.max(0, Number(c.bgCanvasColorOpacity))) : 0.0; const bgColOp = c.bgCanvasColorOpacity !== undefined ? Math.min(1, Math.max(0, Number(c.bgCanvasColorOpacity))) : (inOnara ? 0.8 : 0.09);
const computedBgCol = bgColOp <= 0.001 ? 'transparent' : (bgColOp >= 0.999 ? bgColor : `color-mix(in srgb, ${bgColor} ${Math.round(bgColOp * 100)}%, transparent)`); const computedBgCol = bgColOp <= 0.001 ? 'transparent' : (bgColOp >= 0.999 ? bgColor : `color-mix(in srgb, ${bgColor} ${Math.round(bgColOp * 100)}%, transparent)`);
document.documentElement.style.setProperty('--onara-bg-opacity', onaraOp.toFixed(3)); document.documentElement.style.setProperty('--onara-bg-opacity', onaraOp.toFixed(3));
@@ -4799,8 +5114,12 @@ window.cancelAnimFrame = (function () {
document.documentElement.style.setProperty('--bg-canvas-color', computedBgCol); document.documentElement.style.setProperty('--bg-canvas-color', computedBgCol);
document.documentElement.style.setProperty('--bg-canvas-color-opacity', bgColOp.toFixed(3)); document.documentElement.style.setProperty('--bg-canvas-color-opacity', bgColOp.toFixed(3));
// Method 1 = GPU CSS filter (the classic look); Method 2 = Hybrid (CSS blur + canvas sat/contrast) // Ambient lighting mode: when enabled, soft GPU diffusion blur makes it look identical to YouTube's ambient mode
if (method === 1 || method === 2) { const useAmbient = Number(c.bgAmbientLighting) === 1;
if (useAmbient) {
const ambientBlur = Math.max(30, Math.min(100, Number(c.bgCanvasBlur) || 48));
document.documentElement.style.setProperty('--bg-canvas-filter', `blur(${ambientBlur}px)`);
} else if (method === 1 || method === 2) {
document.documentElement.style.setProperty('--bg-canvas-filter', `blur(${bgBlur}px)`); document.documentElement.style.setProperty('--bg-canvas-filter', `blur(${bgBlur}px)`);
} else { } else {
document.documentElement.style.setProperty('--bg-canvas-filter', 'none'); document.documentElement.style.setProperty('--bg-canvas-filter', 'none');
@@ -4816,7 +5135,7 @@ window.cancelAnimFrame = (function () {
} }
}; };
window.applyBackgroundOpacitySettings = applyBackgroundOpacitySettings; window.applyBackgroundOpacitySettings = applyBackgroundOpacitySettings;
applyBackgroundOpacitySettings(window.audioVisualizerTuning); applyBackgroundOpacitySettings(null);
// Eye Cursor Follower & 3D Tracking // Eye Cursor Follower & 3D Tracking
const mousePos = { x: null, y: null, active: false }; const mousePos = { x: null, y: null, active: false };
@@ -5095,6 +5414,11 @@ window.cancelAnimFrame = (function () {
{ section: 'Video & Ambient Background Canvas', key: 'bgCanvasColor', label: 'Background Color', type: 'color' }, { section: 'Video & Ambient Background Canvas', key: 'bgCanvasColor', label: 'Background Color', type: 'color' },
{ section: 'Video & Ambient Background Canvas', key: 'bgCanvasColorOpacity', label: 'Background Color Opacity', min: 0.00, max: 1.00, step: 0.01, unit: '' }, { section: 'Video & Ambient Background Canvas', key: 'bgCanvasColorOpacity', label: 'Background Color Opacity', min: 0.00, max: 1.00, step: 0.01, unit: '' },
{ section: 'Video & Ambient Background Canvas', key: 'bgCanvasOpacity', label: 'Background Canvas Opacity', min: 0.00, max: 1.00, step: 0.01, unit: '' }, { section: 'Video & Ambient Background Canvas', key: 'bgCanvasOpacity', label: 'Background Canvas Opacity', min: 0.00, max: 1.00, step: 0.01, unit: '' },
{ section: 'Video & Ambient Background Canvas', key: 'bgAmbientLighting', label: 'Ambient Lighting (1=YouTube Glow, 0=Filter)', min: 0, max: 1, step: 1, unit: '' },
{ section: 'Video & Ambient Background Canvas', key: 'bgAmbientIntensity', label: 'Ambient Glow Brightness', min: 0.10, max: 2.50, step: 0.05, unit: 'x' },
{ section: 'Video & Ambient Background Canvas', key: 'bgAmbientSpread', label: 'Ambient Glow Spread / Size', min: 0.30, max: 2.00, step: 0.05, unit: 'x' },
{ section: 'Video & Ambient Background Canvas', key: 'bgAmbientSaturation', label: 'Ambient Glow Saturation', min: 0.50, max: 3.50, step: 0.05, unit: 'x' },
{ section: 'Video & Ambient Background Canvas', key: 'bgAmbientSmoothness', label: 'Ambient Video Transition Speed', min: 0.01, max: 0.25, step: 0.01, unit: '' },
{ section: 'Video & Ambient Background Canvas', key: 'bgBlurMethod', label: 'Blur Engine (0=Canvas 2D, 1=GPU CSS, 2=Hybrid)', min: 0, max: 2, step: 1, unit: '' }, { section: 'Video & Ambient Background Canvas', key: 'bgBlurMethod', label: 'Blur Engine (0=Canvas 2D, 1=GPU CSS, 2=Hybrid)', min: 0, max: 2, step: 1, unit: '' },
{ section: 'Video & Ambient Background Canvas', key: 'bgCanvasBlur', label: 'Blur Radius', min: 0, max: 300, step: 1, unit: 'px' }, { section: 'Video & Ambient Background Canvas', key: 'bgCanvasBlur', label: 'Blur Radius', min: 0, max: 300, step: 1, unit: 'px' },
{ section: 'Video & Ambient Background Canvas', key: 'bgCanvasBrightness', label: 'Brightness', min: 0.10, max: 3.00, step: 0.05, unit: 'x' }, { section: 'Video & Ambient Background Canvas', key: 'bgCanvasBrightness', label: 'Brightness', min: 0.10, max: 3.00, step: 0.05, unit: 'x' },
@@ -5367,13 +5691,17 @@ window.cancelAnimFrame = (function () {
<div class="f0ck-tuner-body"> <div class="f0ck-tuner-body">
<div class="f0ck-tuner-presets-bar"> <div class="f0ck-tuner-presets-bar">
<div style="font-size: 11px; opacity: 0.85; font-weight: 700; display: flex; align-items: center; gap: 6px;"> <div style="font-size: 11px; opacity: 0.85; font-weight: 700; display: flex; align-items: center; gap: 6px;">
<i class="fa-solid fa-film"></i> Video & Onara Background Settings <i class="fa-solid fa-film"></i> Background Settings
</div> </div>
<div class="f0ck-tuner-presets-actions"> <div class="f0ck-tuner-presets-actions">
<button type="button" id="bg-tuner-copy" class="f0ck-tuner-btn-save-preset" title="Copy background settings as JSON"><i class="fa-solid fa-copy"></i> Copy Settings</button> <button type="button" id="bg-tuner-copy" class="f0ck-tuner-btn-save-preset" title="Copy background settings as JSON"><i class="fa-solid fa-copy"></i> Copy Settings</button>
<button type="button" id="bg-tuner-reset" class="f0ck-tuner-btn-factory-reset" title="Reset background settings to defaults"><i class="fa-solid fa-rotate-left"></i> Reset</button> <button type="button" id="bg-tuner-reset" class="f0ck-tuner-btn-factory-reset" title="Reset background settings to defaults"><i class="fa-solid fa-rotate-left"></i> Reset</button>
</div> </div>
</div> </div>
<div class="f0ck-tuner-mode-bar" style="display: flex; gap: 6px; padding: 6px 12px; background: rgba(255,255,255,0.04); border-bottom: 1px solid rgba(255,255,255,0.08);">
<button type="button" id="bg-mode-btn-standard" class="f0ck-tuner-subtab-btn active" style="flex: 1; padding: 6px 10px; font-size: 11px; text-align: center;">Standard View</button>
<button type="button" id="bg-mode-btn-onara" class="f0ck-tuner-subtab-btn" style="flex: 1; padding: 6px 10px; font-size: 11px; text-align: center;"><i class="fa-solid fa-expand"></i> Onara Mode Only</button>
</div>
${bgRowsHtml} ${bgRowsHtml}
</div> </div>
</div> </div>
@@ -5407,7 +5735,7 @@ window.cancelAnimFrame = (function () {
// ── Audio Presets & Events ────────────────────────────────────────────── // ── Audio Presets & Events ──────────────────────────────────────────────
const BUILTIN_PRESETS = { const BUILTIN_PRESETS = {
'Default': getEffectiveDefaultTuning(), 'Default': getEffectiveDefaultTuning(),
'Onara': Object.assign({}, DEFAULT_AUDIO_TUNING), 'Onara': Object.assign({}, DEFAULT_AUDIO_TUNING, DEFAULT_ONARA_TUNING),
'Reactor': { 'Reactor': {
useCustomColor: 0, useCustomColor: 0,
visualizerColor: "#241f31", visualizerColor: "#241f31",
@@ -5995,68 +6323,125 @@ window.cancelAnimFrame = (function () {
}); });
}); });
// ── Background Sliders Event Listeners ───────────────────────────────── // ── Background Sliders Event Listeners (Standard vs Onara mode) ────────
let activeBgTunerMode = isCurrentlyInOnara() ? 'onara' : 'standard';
const syncBgSlidersDisplay = () => {
const isO = activeBgTunerMode === 'onara';
const sourceObj = isO ? (window.onaraTuning || DEFAULT_ONARA_TUNING) : (window.audioVisualizerTuning || DEFAULT_AUDIO_TUNING);
const defaultObj = isO ? DEFAULT_ONARA_TUNING : DEFAULT_AUDIO_TUNING;
const stdBtn = panel.querySelector('#bg-mode-btn-standard');
const onaraBtn = panel.querySelector('#bg-mode-btn-onara');
if (stdBtn) stdBtn.classList.toggle('active', !isO);
if (onaraBtn) onaraBtn.classList.toggle('active', isO);
backgroundSliders.forEach(s => {
const input = panel.querySelector(`#input-bg-${s.key}`);
const valEl = panel.querySelector(`#val-bg-${s.key}`);
if (!input || !valEl) return;
const val = sourceObj[s.key] !== undefined ? sourceObj[s.key] : defaultObj[s.key];
input.value = val;
if (s.type === 'color') {
valEl.textContent = val;
} else {
const num = Number(val);
const displayVal = Number.isInteger(num) ? num : parseFloat(num.toFixed(3));
valEl.textContent = `${displayVal}${s.unit || ''}`;
}
});
};
panel.querySelector('#bg-mode-btn-standard')?.addEventListener('click', () => {
activeBgTunerMode = 'standard';
syncBgSlidersDisplay();
applyBackgroundOpacitySettings(window.audioVisualizerTuning, false);
if (typeof window.flashMessage === 'function') {
window.flashMessage('Tuning Standard Background', 1500, 'info');
}
});
panel.querySelector('#bg-mode-btn-onara')?.addEventListener('click', () => {
activeBgTunerMode = 'onara';
syncBgSlidersDisplay();
applyBackgroundOpacitySettings(window.onaraTuning, true);
if (typeof window.flashMessage === 'function') {
window.flashMessage('Tuning Onara Mode Background', 1500, 'info');
}
});
backgroundSliders.forEach(s => { backgroundSliders.forEach(s => {
const input = panel.querySelector(`#input-bg-${s.key}`); const input = panel.querySelector(`#input-bg-${s.key}`);
const valEl = panel.querySelector(`#val-bg-${s.key}`); const valEl = panel.querySelector(`#val-bg-${s.key}`);
if (!input || !valEl) return; if (!input || !valEl) return;
input.addEventListener('input', () => { input.addEventListener('input', () => {
const isO = activeBgTunerMode === 'onara';
const targetObj = isO ? window.onaraTuning : window.audioVisualizerTuning;
const storageKey = isO ? 'f0ck_onara_tuning' : 'f0ck_audio_tuning';
if (s.type === 'color') { if (s.type === 'color') {
window.audioVisualizerTuning[s.key] = input.value; targetObj[s.key] = input.value;
valEl.textContent = input.value; valEl.textContent = input.value;
} else { } else {
const parsed = parseFloat(input.value); const parsed = parseFloat(input.value);
window.audioVisualizerTuning[s.key] = parsed; targetObj[s.key] = parsed;
const displayVal = Number.isInteger(parsed) ? parsed : parseFloat(parsed.toFixed(3)); const displayVal = Number.isInteger(parsed) ? parsed : parseFloat(parsed.toFixed(3));
valEl.textContent = `${displayVal}${s.unit || ''}`; valEl.textContent = `${displayVal}${s.unit || ''}`;
} }
applyBackgroundOpacitySettings(window.audioVisualizerTuning); applyBackgroundOpacitySettings(targetObj, isO);
try { try {
localStorage.setItem('f0ck_audio_tuning', JSON.stringify(window.audioVisualizerTuning)); localStorage.setItem(storageKey, JSON.stringify(targetObj));
} catch (e) {} } catch (e) {}
}); });
}); });
panel.querySelector('#bg-tuner-reset')?.addEventListener('click', () => { panel.querySelector('#bg-tuner-reset')?.addEventListener('click', () => {
const isO = activeBgTunerMode === 'onara';
if (isO) {
window.onaraTuning = Object.assign({}, DEFAULT_ONARA_TUNING);
try {
localStorage.setItem('f0ck_onara_tuning', JSON.stringify(window.onaraTuning));
} catch (e) {}
syncBgSlidersDisplay();
applyBackgroundOpacitySettings(window.onaraTuning, true);
if (typeof window.flashMessage === 'function') {
window.flashMessage('Onara background settings reset to defaults', 2000, 'info');
}
} else {
const defaults = DEFAULT_AUDIO_TUNING; const defaults = DEFAULT_AUDIO_TUNING;
backgroundSliders.forEach(s => { backgroundSliders.forEach(s => {
window.audioVisualizerTuning[s.key] = defaults[s.key]; window.audioVisualizerTuning[s.key] = defaults[s.key];
const input = panel.querySelector(`#input-bg-${s.key}`);
const valEl = panel.querySelector(`#val-bg-${s.key}`);
if (input) input.value = defaults[s.key];
if (valEl) {
if (s.type === 'color') {
valEl.textContent = defaults[s.key];
} else {
const num = Number(defaults[s.key]);
const displayVal = Number.isInteger(num) ? num : parseFloat(num.toFixed(3));
valEl.textContent = `${displayVal}${s.unit || ''}`;
}
}
}); });
applyBackgroundOpacitySettings(window.audioVisualizerTuning); syncBgSlidersDisplay();
applyBackgroundOpacitySettings(window.audioVisualizerTuning, false);
try { try {
localStorage.setItem('f0ck_audio_tuning', JSON.stringify(window.audioVisualizerTuning)); localStorage.setItem('f0ck_audio_tuning', JSON.stringify(window.audioVisualizerTuning));
} catch (e) {} } catch (e) {}
if (typeof window.flashMessage === 'function') { if (typeof window.flashMessage === 'function') {
window.flashMessage('Background settings reset to default', 2000, 'info'); window.flashMessage('Standard background settings reset to default', 2000, 'info');
}
} }
}); });
panel.querySelector('#bg-tuner-copy')?.addEventListener('click', () => { panel.querySelector('#bg-tuner-copy')?.addEventListener('click', () => {
const isO = activeBgTunerMode === 'onara';
const targetObj = isO ? window.onaraTuning : window.audioVisualizerTuning;
const bgCfg = {}; const bgCfg = {};
backgroundSliders.forEach(s => { backgroundSliders.forEach(s => {
bgCfg[s.key] = window.audioVisualizerTuning[s.key]; bgCfg[s.key] = targetObj[s.key];
}); });
navigator.clipboard.writeText(JSON.stringify(bgCfg, null, 2)).then(() => { navigator.clipboard.writeText(JSON.stringify(bgCfg, null, 2)).then(() => {
if (typeof window.flashMessage === 'function') { if (typeof window.flashMessage === 'function') {
window.flashMessage('Background settings copied to clipboard!', 2500, 'success'); window.flashMessage(`${isO ? 'Onara' : 'Standard'} background settings copied to clipboard!`, 2500, 'success');
} }
}); });
}); });
// Initialize initial slider state in tuner
syncBgSlidersDisplay();
panel.querySelector('#f0ck-tuner-copy')?.addEventListener('click', () => { panel.querySelector('#f0ck-tuner-copy')?.addEventListener('click', () => {
const copyBtn = panel.querySelector('#f0ck-tuner-copy'); const copyBtn = panel.querySelector('#f0ck-tuner-copy');
const text = JSON.stringify(window.audioVisualizerTuning, null, 2); const text = JSON.stringify(window.audioVisualizerTuning, null, 2);
@@ -7032,7 +7417,7 @@ window.cancelAnimFrame = (function () {
const bOpacity = Math.min(1, Math.max(0, cfg.barOpacity !== undefined ? Number(cfg.barOpacity) : 0.85)); const bOpacity = Math.min(1, Math.max(0, cfg.barOpacity !== undefined ? Number(cfg.barOpacity) : 0.85));
const bGlow = Math.max(0, cfg.barGlow !== undefined ? cfg.barGlow : 8); const bGlow = Math.max(0, cfg.barGlow !== undefined ? cfg.barGlow : 8);
const feedToBg = Number(cfg.feedAudioToBg !== undefined ? cfg.feedAudioToBg : 0) === 1; const feedToBg = Number(cfg.feedAudioToBg !== undefined ? cfg.feedAudioToBg : 1) === 1;
const showBarsOnPlayer = cfg.enableBars !== undefined ? Number(cfg.enableBars) === 1 : true; const showBarsOnPlayer = cfg.enableBars !== undefined ? Number(cfg.enableBars) === 1 : true;
const showBars = showBarsOnPlayer || feedToBg; const showBars = showBarsOnPlayer || feedToBg;
canvas.style.opacity = showBarsOnPlayer ? '1' : '0'; canvas.style.opacity = showBarsOnPlayer ? '1' : '0';
@@ -8686,7 +9071,7 @@ window.cancelAnimFrame = (function () {
// Draw inner eye canvas centered behind the item content (respecting sidebar width) // Draw inner eye canvas centered behind the item content (respecting sidebar width)
const srcW = innerEyeCanvas.width; const srcW = innerEyeCanvas.width;
const srcH = innerEyeCanvas.height; const srcH = innerEyeCanvas.height;
const bgScale = cfg.bgCanvasScale !== undefined ? Number(cfg.bgCanvasScale) : 3; const bgScale = cfg.bgCanvasScale !== undefined ? Number(cfg.bgCanvasScale) : 2.15;
// #bg canvas is already positioned to the free space respecting the sidebar // #bg canvas is already positioned to the free space respecting the sidebar
const contentW = bw; const contentW = bw;
@@ -8698,8 +9083,8 @@ window.cancelAnimFrame = (function () {
const dy = (bh - dh) / 2; const dy = (bh - dh) / 2;
// Apply blur & brightness via CSS filter (smooth at display resolution) // Apply blur & brightness via CSS filter (smooth at display resolution)
const blurPx = cfg.bgCanvasBlur !== undefined ? Number(cfg.bgCanvasBlur) : 30; const blurPx = cfg.bgCanvasBlur !== undefined ? Number(cfg.bgCanvasBlur) : 31;
const brightVal = cfg.bgCanvasBrightness !== undefined ? Number(cfg.bgCanvasBrightness) : 0.45; const brightVal = cfg.bgCanvasBrightness !== undefined ? Number(cfg.bgCanvasBrightness) : 0.6;
const filterParts = []; const filterParts = [];
if (blurPx > 0) filterParts.push(`blur(${blurPx}px)`); if (blurPx > 0) filterParts.push(`blur(${blurPx}px)`);
if (brightVal !== 1) filterParts.push(`brightness(${brightVal})`); if (brightVal !== 1) filterParts.push(`brightness(${brightVal})`);
@@ -10384,7 +10769,7 @@ window.cancelAnimFrame = (function () {
canvas.classList.remove('fader-out', 'fast-fade'); canvas.classList.remove('fader-out', 'fast-fade');
canvas.classList.add('fader-in'); canvas.classList.add('fader-in');
canvas.style.transition = 'none'; canvas.style.transition = 'none';
canvas.style.opacity = 'var(--bg-canvas-opacity, 1.0)'; canvas.style.opacity = 'var(--bg-canvas-opacity, 0.95)';
} else { } else {
canvas.classList.add('fast-fade'); canvas.classList.add('fast-fade');
canvas.classList.remove('fader-in'); canvas.classList.remove('fader-in');
@@ -10655,9 +11040,13 @@ window.cancelAnimFrame = (function () {
const _newEls = _container.querySelectorAll('.content, ._204863, .item_title'); const _newEls = _container.querySelectorAll('.content, ._204863, .item_title');
_newEls.forEach(el => { _newEls.forEach(el => {
el.classList.remove('slide-next', 'slide-prev', 'slide-out-left', 'slide-out-right', 'fade-out-zoom', 'fade-in-zoom'); el.classList.remove('slide-next', 'slide-prev', 'slide-out-left', 'slide-out-right', 'fade-out-zoom', 'fade-in-zoom');
if (useOnara) {
el.classList.add('fade-in-zoom');
} else {
el.classList.add(options.direction === 'next' ? 'slide-next' : 'slide-prev'); el.classList.add(options.direction === 'next' ? 'slide-next' : 'slide-prev');
}
}); });
} else if (options.transition === 'fade-zoom') { } else if (options.transition === 'fade-zoom' || useOnara) {
const _newEls = _container.querySelectorAll('.content, ._204863, .item_title'); const _newEls = _container.querySelectorAll('.content, ._204863, .item_title');
_newEls.forEach(el => { _newEls.forEach(el => {
el.classList.remove('slide-next', 'slide-prev', 'slide-out-left', 'slide-out-right', 'fade-out-zoom', 'fade-in-zoom'); el.classList.remove('slide-next', 'slide-prev', 'slide-out-left', 'slide-out-right', 'fade-out-zoom', 'fade-in-zoom');
@@ -10701,7 +11090,7 @@ window.cancelAnimFrame = (function () {
_canvas.classList.add('fader-in'); _canvas.classList.add('fader-in');
if (useOnara) { if (useOnara) {
_canvas.style.transition = 'none'; _canvas.style.transition = 'none';
_canvas.style.opacity = 'var(--bg-canvas-opacity, 1.0)'; _canvas.style.opacity = 'var(--bg-canvas-opacity, 0.95)';
} }
} }
if (window.initBackground) window.initBackground(); if (window.initBackground) window.initBackground();
@@ -10903,9 +11292,13 @@ window.cancelAnimFrame = (function () {
const newEls = container.querySelectorAll('.content, ._204863, .item_title'); const newEls = container.querySelectorAll('.content, ._204863, .item_title');
newEls.forEach(el => { newEls.forEach(el => {
el.classList.remove('slide-next', 'slide-prev', 'slide-out-left', 'slide-out-right', 'fade-out-zoom', 'fade-in-zoom'); el.classList.remove('slide-next', 'slide-prev', 'slide-out-left', 'slide-out-right', 'fade-out-zoom', 'fade-in-zoom');
if (useOnara) {
el.classList.add('fade-in-zoom');
} else {
el.classList.add(options.direction === 'next' ? 'slide-next' : 'slide-prev'); el.classList.add(options.direction === 'next' ? 'slide-next' : 'slide-prev');
}
}); });
} else if (options.transition === 'fade-zoom') { } else if (options.transition === 'fade-zoom' || useOnara) {
const newEls = container.querySelectorAll('.content, ._204863, .item_title'); const newEls = container.querySelectorAll('.content, ._204863, .item_title');
newEls.forEach(el => { newEls.forEach(el => {
el.classList.remove('slide-next', 'slide-prev', 'slide-out-left', 'slide-out-right', 'fade-out-zoom', 'fade-in-zoom'); el.classList.remove('slide-next', 'slide-prev', 'slide-out-left', 'slide-out-right', 'fade-out-zoom', 'fade-in-zoom');
@@ -10975,7 +11368,7 @@ window.cancelAnimFrame = (function () {
canvas.classList.add('fader-in'); canvas.classList.add('fader-in');
if (useOnara) { if (useOnara) {
canvas.style.transition = 'none'; canvas.style.transition = 'none';
canvas.style.opacity = 'var(--bg-canvas-opacity, 1.0)'; canvas.style.opacity = 'var(--bg-canvas-opacity, 0.95)';
} }
} }
if (window.initBackground) window.initBackground(); if (window.initBackground) window.initBackground();
@@ -11121,13 +11514,14 @@ window.cancelAnimFrame = (function () {
if (thumbnail && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey) { if (thumbnail && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey) {
e.preventDefault(); e.preventDefault();
if (isOnaraActive()) { if (isOnaraActive()) {
const wasOpen = document.body.classList.contains('onara-modal-open');
document.querySelectorAll('.onara-active').forEach(el => el.classList.remove('onara-active')); document.querySelectorAll('.onara-active').forEach(el => el.classList.remove('onara-active'));
thumbnail.classList.add('onara-active'); thumbnail.classList.add('onara-active');
if (document.activeElement && document.activeElement !== thumbnail && typeof document.activeElement.blur === 'function') { if (document.activeElement && document.activeElement !== thumbnail && typeof document.activeElement.blur === 'function') {
document.activeElement.blur(); document.activeElement.blur();
} }
openOnaraModal(); openOnaraModal();
if (typeof window.paintImmediateBgThumb === 'function') { if (!wasOpen && typeof window.paintImmediateBgThumb === 'function') {
window.paintImmediateBgThumb(thumbnail); window.paintImmediateBgThumb(thumbnail);
} }
} }
@@ -11299,18 +11693,26 @@ window.cancelAnimFrame = (function () {
const isPrev = link.id === 'prev' || link.classList.contains('nav-prev'); const isPrev = link.id === 'prev' || link.classList.contains('nav-prev');
const direction = isNext ? 'next' : (isPrev ? 'prev' : null); const direction = isNext ? 'next' : (isPrev ? 'prev' : null);
const isModalOpen = document.body.classList.contains('onara-modal-open');
const isGridPage = !!document.querySelector('.index-layout-wrapper, .index-container, .posts');
const useOnara = isOnaraActive() && (isGridPage || isModalOpen);
if (direction) { if (direction) {
const outEls = document.querySelectorAll('.content, ._204863, .item_title'); const outEls = document.querySelectorAll('.content, ._204863, .item_title');
outEls.forEach(el => { outEls.forEach(el => {
el.classList.remove('slide-out-left', 'slide-out-right', 'slide-next', 'slide-prev'); el.classList.remove('slide-out-left', 'slide-out-right', 'slide-next', 'slide-prev', 'fade-out-zoom', 'fade-in-zoom');
if (useOnara) {
el.classList.add('fade-out-zoom');
} else {
el.classList.add(direction === 'next' ? 'slide-out-left' : 'slide-out-right'); el.classList.add(direction === 'next' ? 'slide-out-left' : 'slide-out-right');
}
}); });
} }
if (link.href.match(/\/p\/\d+/) || link.href.match(/[?&]page=\d+/)) { if (link.href.match(/\/p\/\d+/) || link.href.match(/[?&]page=\d+/)) {
loadPageAjax(link.href); loadPageAjax(link.href);
} else { } else {
loadItemAjax(link.href, true, { direction }); loadItemAjax(link.href, true, { direction, transition: useOnara ? 'fade-zoom' : null });
} }
return; return;
} }