File size: 4,897 Bytes
7ff860b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// DistractIQ deterministic prediction engine
// Implements the spec formulas exactly.

export interface FocusInputs {
  study_hours: number;
  phone_hours: number;
  social_hours: number;
  sleep_hours: number;
  stress_level: number; // 1-10
  notifications: number;
}

export interface FocusOutputs {
  distraction_score: number;
  productivity: number;
  focus_score: number;
  risk: "HIGH" | "LOW";
  risk_reasons: string[];
  xp_gained: number;
  deep_focus_hours: number;
  advisories: string[];
  persona: { title: string; caption: string; emoji: string };
}

const clamp = (v: number, min: number, max: number) => Math.max(min, Math.min(max, v));

export function computeDistraction(i: FocusInputs): number {
  const raw = i.phone_hours * 8 + i.social_hours * 12 + i.notifications / 10 + i.stress_level * 5;
  return Math.round(clamp(raw, 0, 100));
}

export function computeProductivity(i: FocusInputs): number {
  const raw = i.study_hours * 15 + i.sleep_hours * 5 - (i.phone_hours * 4 + i.social_hours * 6 + i.stress_level * 8) + 30;
  return Math.round(clamp(raw, 0, 100));
}

export function computeFocusScore(distraction: number, productivity: number): number {
  // Half-doughnut: invert distraction, weight productivity
  return Math.round(clamp(productivity * 0.6 + (100 - distraction) * 0.4, 0, 100));
}

export function computeRisk(i: FocusInputs, distraction: number): { risk: "HIGH" | "LOW"; reasons: string[] } {
  const reasons: string[] = [];
  if (i.phone_hours > 6 && i.study_hours < 3) reasons.push("Phone use exceeds 6h with under 3h of study");
  if (i.social_hours > 4) reasons.push("Social media exceeds 4h");
  if (i.stress_level > 8 && i.sleep_hours < 5) reasons.push("High stress combined with sleep deprivation");
  if (distraction > 65) reasons.push("Distraction Score above critical threshold (65)");
  return { risk: reasons.length > 0 ? "HIGH" : "LOW", reasons };
}

export function computeAdvisories(i: FocusInputs): string[] {
  const out: string[] = [];
  if (i.stress_level > 7) out.push("Stress is blocking your deep work capacity.");
  if (i.sleep_hours < 6) out.push("Sleep deprivation is a hidden productivity killer.");
  if (i.notifications > 150) out.push("Your notification density is triggering severe context-switching.");
  if (i.phone_hours > 6) out.push("Excessive phone time is fragmenting your attention.");
  if (i.social_hours > 4) out.push("Social media is the leading cause of dopamine loops today.");
  return out;
}

export function derivePersona(focus: number, productivity: number, distraction: number): { title: string; caption: string; emoji: string } {
  if (focus >= 80) return { title: "Deep Work Ninja", emoji: "🥷", caption: "Silent. Focused. Untouchable. You operate in flow others only read about." };
  if (focus >= 65) return { title: "Momentum Maker", emoji: "🚀", caption: "Solid rhythm — protect your peak hours and you'll hit elite tier." };
  if (productivity >= 50 && distraction < 50) return { title: "Balanced Builder", emoji: "⚖️", caption: "You're stitching focus and rest together — keep refining the edges." };
  if (distraction >= 70) return { title: "Scroll Hostage", emoji: "🪤", caption: "The feed is winning. Time to reclaim the next 25 minutes." };
  if (productivity < 35) return { title: "Drift Mode", emoji: "🌫️", caption: "Low signal day. One small block of deep work resets the whole week." };
  return { title: "Focus Apprentice", emoji: "🎯", caption: "Building the habit. Small, repeatable wins compound fast." };
}

export function computeAll(i: FocusInputs): FocusOutputs {
  const distraction = computeDistraction(i);
  const productivity = computeProductivity(i);
  const focus = computeFocusScore(distraction, productivity);
  const { risk, reasons } = computeRisk(i, distraction);
  const xp = Math.max(0, Math.round(productivity / 3));
  return {
    distraction_score: distraction,
    productivity,
    focus_score: focus,
    risk,
    risk_reasons: reasons,
    xp_gained: xp,
    deep_focus_hours: Math.round(i.study_hours * (productivity / 100) * 10) / 10,
    advisories: computeAdvisories(i),
    persona: derivePersona(focus, productivity, distraction),
  };
}

// Constraint validation
export function validateConstraints(i: FocusInputs): string[] {
  const errors: string[] = [];
  if (i.study_hours + i.phone_hours + i.sleep_hours > 24) {
    errors.push("Study + Phone + Sleep cannot exceed 24 hours.");
  }
  if (i.social_hours > i.phone_hours) {
    errors.push("Social media hours cannot exceed total phone hours.");
  }
  return errors;
}

// XP / level helpers
export const XP_PER_LEVEL = 500;
export const levelFromXp = (xp: number) => Math.floor(xp / XP_PER_LEVEL) + 1;
export const xpInLevel = (xp: number) => xp % XP_PER_LEVEL;

export function isNightMode(): boolean {
  const hour = new Date().getHours();
  return hour >= 22 || hour < 5;
}