Update to v1.0.3: Guided Relaxation & UI Fixes

This commit is contained in:
Gemini AI
2025-12-06 23:49:58 +04:00
Unverified
parent 5e9ffe1997
commit 7cc3c0e3c0
4 changed files with 266 additions and 23 deletions

View File

@@ -1190,30 +1190,171 @@ function startExercise(type) {
}
function quickRelax() {
const techniques = [
{ icon: '🌬️', title: 'Box Breathing', desc: 'Inhale 4s, Hold 4s, Exhale 4s, Hold 4s' },
{ icon: '👁️', title: '5-4-3-2-1 Grounding', desc: 'Name 5 things you see, 4 feel, 3 hear...' },
{ icon: '💆', title: 'Muscle Relaxation', desc: 'Tense and release each muscle group' },
{ icon: '🌊', title: 'Visualisation', desc: 'Imagine a peaceful place in detail' }
];
// Launch the new Guided Grounding Experience
startGuidedRelaxation();
}
// --- Guided Relaxation (5-4-3-2-1 Grounding) ---
let relaxationState = {
step: 0,
isActive: false,
steps: [
{
title: "Sight",
instruction: "Look around you.",
sub: "Find 5 things you can see.",
count: 5,
icon: "👁️",
color: "#64B5F6"
},
{
title: "Touch",
instruction: "Feel the textures.",
sub: "Find 4 things you can touch.",
count: 4,
icon: "✋",
color: "#81C784"
},
{
title: "Sound",
instruction: "Listen carefully.",
sub: "Identify 3 sounds you hear.",
count: 3,
icon: "👂",
color: "#FFB74D"
},
{
title: "Smell",
instruction: "Breathe in deep.",
sub: "Notice 2 things you can smell.",
count: 2,
icon: "👃",
color: "#BA68C8"
},
{
title: "Taste",
instruction: "Focus on your mouth.",
sub: "Find 1 thing you can taste.",
count: 1,
icon: "👅",
color: "#E57373"
}
]
};
function startGuidedRelaxation() {
relaxationState.step = 0;
relaxationState.isActive = true;
const randomTechnique = techniques[Math.floor(Math.random() * techniques.length)];
const overlay = document.createElement('div');
overlay.id = 'guided-relaxation-overlay';
overlay.className = 'guided-overlay';
document.body.appendChild(overlay);
const modal = document.createElement('div');
modal.className = 'exercise-modal';
modal.style.display = 'block';
modal.innerHTML = `
<div class="card" style="text-align: center;">
<div style="font-size: 60px; margin-bottom: 20px; animation: bounce 2s infinite;">${randomTechnique.icon}</div>
<h3>${randomTechnique.title}</h3>
<p style="font-size: 18px; margin-bottom: 30px;">${randomTechnique.desc}</p>
<div class="exercise-actions" style="justify-content: center;">
<button class="btn btn-primary" onclick="this.closest('.exercise-modal').remove(); quickRelax()">Try Another</button>
<button class="btn btn-secondary" onclick="finishRelaxSession(this)">Done</button>
</div>
renderRelaxationStep();
}
function renderRelaxationStep() {
const overlay = document.getElementById('guided-relaxation-overlay');
if (!overlay || !relaxationState.isActive) return;
const currentStep = relaxationState.steps[relaxationState.step];
const progressDots = Array(currentStep.count).fill('<div class="progress-dot"></div>').join('');
overlay.innerHTML = `
<div class="guided-controls">
<button class="icon-btn" onclick="closeGuidedRelaxation()">
<span class="material-icons">close</span>
</button>
</div>
<div class="guided-step-icon" style="color: ${currentStep.color}">
${currentStep.icon}
</div>
<div class="guided-instruction">${currentStep.instruction}</div>
<div class="guided-sub">${currentStep.sub}</div>
<div class="guided-progress-dots" id="step-dots">
${progressDots}
</div>
<button class="guided-action-btn" onclick="nextRelaxationSubStep()">
I found one
</button>
`;
document.body.appendChild(modal);
// Speak instruction
speakText(`${currentStep.instruction} ${currentStep.sub}`);
}
let currentDotIndex = 0;
function nextRelaxationSubStep() {
const dots = document.querySelectorAll('.progress-dot');
if (currentDotIndex < dots.length) {
dots[currentDotIndex].classList.add('active');
// Haptic feedback
if (navigator.vibrate) navigator.vibrate(50);
// Sound feedback
soundManager.playTone(400 + (currentDotIndex * 50), 'sine', 0.1, 0.1);
currentDotIndex++;
if (currentDotIndex === dots.length) {
setTimeout(() => {
currentDotIndex = 0;
relaxationState.step++;
if (relaxationState.step < relaxationState.steps.length) {
renderRelaxationStep();
} else {
finishGuidedRelaxation();
}
}, 1000);
}
}
}
function finishGuidedRelaxation() {
const overlay = document.getElementById('guided-relaxation-overlay');
if (overlay) {
overlay.innerHTML = `
<div class="guided-step-icon">🌟</div>
<div class="guided-instruction">You did great!</div>
<div class="guided-sub">Feeling more grounded?</div>
<button class="guided-action-btn" onclick="closeGuidedRelaxation()">
Complete
</button>
`;
speakText("You did great. Feeling more grounded?");
triggerSuccessPing();
}
}
function closeGuidedRelaxation() {
relaxationState.isActive = false;
currentDotIndex = 0;
const overlay = document.getElementById('guided-relaxation-overlay');
if (overlay) overlay.remove();
if (window.speechSynthesis) {
window.speechSynthesis.cancel();
}
// Log session
if (relaxationState.step >= relaxationState.steps.length) {
exerciseAPI.logSession('grounding', 180).then(() => updateProgress());
}
}
function speakText(text) {
if ('speechSynthesis' in window) {
window.speechSynthesis.cancel(); // Stop previous
const utterance = new SpeechSynthesisUtterance(text);
utterance.rate = 0.9;
utterance.pitch = 1.0;
window.speechSynthesis.speak(utterance);
}
}
function finishRelaxSession(btn) {