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

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{
"name": "mindshift-cbt-therapy",
"version": "1.0.2",
"version": "1.0.3",
"description": "MindShift - Your personal CBT therapy companion for Windows 11",
"main": "src/main.js",
"homepage": "./",

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();
}
const randomTechnique = techniques[Math.floor(Math.random() * techniques.length)];
// --- 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"
}
]
};
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>
function startGuidedRelaxation() {
relaxationState.step = 0;
relaxationState.isActive = true;
const overlay = document.createElement('div');
overlay.id = 'guided-relaxation-overlay';
overlay.className = 'guided-overlay';
document.body.appendChild(overlay);
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) {

View File

@@ -255,13 +255,115 @@ body {
display: flex;
justify-content: space-around;
padding: 12px 0;
/* Android Safe Area Fix - Increased Padding */
padding-bottom: calc(24px + env(safe-area-inset-bottom));
/* Android Safe Area Fix - Significantly Increased Padding */
padding-bottom: calc(40px + env(safe-area-inset-bottom));
z-index: 100;
border-top-left-radius: 24px;
border-top-right-radius: 24px;
}
/* Guided Relaxation Styles */
.guided-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(circle at center, #2E7D32, #004D40);
z-index: 6000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
animation: fadeIn 0.5s ease;
padding: 20px;
text-align: center;
}
.guided-step-icon {
font-size: 80px;
margin-bottom: 30px;
animation: floatIcon 3s ease-in-out infinite;
filter: drop-shadow(0 0 20px rgba(255,255,255,0.3));
}
@keyframes floatIcon {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-15px); }
}
.guided-instruction {
font-size: 28px;
font-weight: 300;
margin-bottom: 16px;
line-height: 1.4;
}
.guided-sub {
font-size: 18px;
opacity: 0.8;
margin-bottom: 40px;
max-width: 80%;
}
.guided-progress-dots {
display: flex;
gap: 8px;
margin-bottom: 40px;
}
.progress-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
transition: all 0.3s;
}
.progress-dot.active {
background: white;
transform: scale(1.2);
}
.guided-action-btn {
background: white;
color: #004D40;
border: none;
padding: 16px 40px;
border-radius: 50px;
font-size: 18px;
font-weight: bold;
cursor: pointer;
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
transition: transform 0.2s;
}
.guided-action-btn:active {
transform: scale(0.95);
}
.guided-controls {
position: absolute;
top: 20px;
right: 20px;
display: flex;
gap: 16px;
}
.icon-btn {
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
width: 44px;
height: 44px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.nav-item {
display: flex;
flex-direction: column;