namish10 commited on
Commit
532fcb5
·
verified ·
1 Parent(s): 81e5178

Upload index.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +611 -218
index.html CHANGED
@@ -1,270 +1,663 @@
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
- <title>ContextFlow OpenEnv</title>
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
 
 
6
  <style>
7
- * { box-sizing: border-box; }
8
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 1000px; margin: 0 auto; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; }
9
- .container { background: white; border-radius: 20px; padding: 30px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); }
10
- h1 { color: #667eea; text-align: center; margin-bottom: 5px; }
11
- .subtitle { text-align: center; color: #888; margin-bottom: 30px; }
12
- h2 { color: #333; border-bottom: 3px solid #667eea; padding-bottom: 10px; margin-top: 30px; }
13
- .card { background: #f8f9fa; padding: 20px; border-radius: 15px; margin: 15px 0; }
14
- .controls { display: flex; gap: 15px; flex-wrap: wrap; align-items: center; margin: 15px 0; }
15
- button { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 15px 30px; border-radius: 10px; cursor: pointer; font-size: 16px; font-weight: bold; transition: transform 0.2s, box-shadow 0.2s; }
16
- button:hover { transform: translateY(-2px); box-shadow: 0 5px 20px rgba(102,126,234,0.4); }
17
- button:disabled { background: #ccc; cursor: not-allowed; transform: none; }
18
- select, input[type="range"] { padding: 12px 15px; border: 2px solid #e0e0e0; border-radius: 10px; font-size: 16px; }
19
- .result { background: #1a1a2e; color: #0f0; padding: 20px; border-radius: 10px; margin: 15px 0; font-family: 'Courier New', monospace; font-size: 14px; line-height: 1.6; max-height: 400px; overflow-y: auto; white-space: pre-wrap; word-wrap: break-word; }
20
- .metrics { display: flex; gap: 10px; flex-wrap: wrap; margin: 15px 0; }
21
- .metric { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 10px 20px; border-radius: 25px; font-weight: bold; }
22
- .status { text-align: center; padding: 10px; border-radius: 10px; margin: 10px 0; font-weight: bold; }
23
- .status.ready { background: #d4edda; color: #155724; }
24
- .status.error { background: #f8d7da; color: #721c24; }
25
- .status.loading { background: #fff3cd; color: #856404; }
26
- .agent-info { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin: 15px 0; }
27
- .agent-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px; border-radius: 10px; text-align: center; }
28
- .agent-card h3 { margin: 0 0 5px 0; font-size: 14px; }
29
- .agent-card p { margin: 0; font-size: 24px; font-weight: bold; }
30
- .loading { text-align: center; padding: 50px; }
31
- .spinner { border: 4px solid #f3f3f3; border-top: 4px solid #667eea; border-radius: 50%; width: 50px; height: 50px; animation: spin 1s linear infinite; margin: 0 auto; }
32
- @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  </style>
34
  </head>
35
  <body>
36
- <div class="container">
37
- <h1>ContextFlow OpenEnv</h1>
38
- <p class="subtitle">Learning Confusion Prediction with RL Agents</p>
39
-
40
- <div id="status" class="status loading">Connecting to ContextFlow API...</div>
41
-
42
- <div class="card">
43
- <h2>Environment Setup</h2>
44
- <div class="controls">
45
- <select id="difficulty">
46
- <option value="easy">Easy (50 steps, threshold: 0.5)</option>
47
- <option value="medium" selected>Medium (75 steps, threshold: 0.6)</option>
48
- <option value="hard">Hard (100 steps, threshold: 0.7)</option>
49
- </select>
50
- <button onclick="reset()">Reset Environment</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  </div>
53
 
54
- <div class="card">
55
- <h2>Make Prediction</h2>
56
- <div class="controls">
57
- <label>Predicted Confusion:</label>
58
- <input type="range" id="confusion" min="0" max="1" step="0.05" value="0.5">
59
- <span id="confusionValue" style="font-size:24px;font-weight:bold;color:#667eea;">0.50</span>
60
  </div>
61
- <div class="controls">
62
- <label>Intervention:</label>
63
- <select id="intervention">
64
- <option value="">None</option>
65
- <option value="hint">Hint</option>
66
- <option value="simplify">Simplify</option>
67
- <option value="breakdown">Breakdown</option>
68
- <option value="example">Example</option>
69
- <option value="scaffold">Scaffold</option>
70
- <option value="peer_connect">Peer Connect</option>
71
- <option value="break">Break</option>
72
- <option value="encourage">Encourage</option>
73
- </select>
74
  </div>
75
- <button id="stepBtn" onclick="step()" disabled>Step</button>
76
- </div>
77
-
78
- <div class="card">
79
- <h2>Results</h2>
80
- <div id="result" class="result">Click "Reset Environment" to start...</div>
81
- </div>
82
-
83
- <div class="card">
84
- <h2>Metrics</h2>
85
- <div class="metrics" id="metrics"></div>
86
  </div>
87
 
88
- <div class="card">
89
- <h2>Active ContextFlow Agents</h2>
90
- <div class="agent-info">
91
- <div class="agent-card">
92
- <h3>RL Doubt Predictor</h3>
93
- <p>Q-Learning</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  </div>
95
- <div class="agent-card">
96
- <h3>Behavioral Analyzer</h3>
97
- <p>Signal Fusion</p>
 
 
 
 
 
98
  </div>
99
- <div class="agent-card">
100
- <h3>Knowledge Graph</h3>
101
- <p>Prerequisites</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  </div>
103
- <div class="agent-card">
104
- <h3>Gesture Recognizer</h3>
105
- <p>Hand Tracking</p>
 
 
 
 
 
 
 
 
106
  </div>
107
  </div>
108
  </div>
109
  </div>
110
 
111
  <script>
112
- const API_BASE = 'https://namish10-contextflow-env-api.hf.space';
113
- let episodeId = null;
114
- let stepCount = 0;
115
- let episodeReward = 0;
116
- let currentDifficulty = 'medium';
117
-
118
- const confusionSlider = document.getElementById('confusion');
119
- const confusionValue = document.getElementById('confusionValue');
120
- const statusEl = document.getElementById('status');
121
- const resultEl = document.getElementById('result');
122
- const metricsEl = document.getElementById('metrics');
123
- const stepBtn = document.getElementById('stepBtn');
124
-
125
- confusionSlider.oninput = () => {
126
- confusionValue.textContent = parseFloat(confusionSlider.value).toFixed(2);
 
 
 
 
127
  };
128
 
129
- async function checkAPI() {
130
- try {
131
- const resp = await fetch(API_BASE + '/health');
132
- const data = await resp.json();
133
- statusEl.className = 'status ready';
134
- statusEl.textContent = 'Connected to ContextFlow API - ' + data.status;
135
- } catch (e) {
136
- statusEl.className = 'status error';
137
- statusEl.textContent = 'API Connection Failed: ' + e.message;
138
- }
 
 
 
 
 
 
 
 
 
 
139
  }
140
 
141
- async function reset() {
142
- currentDifficulty = document.getElementById('difficulty').value;
143
- statusEl.className = 'status loading';
144
- statusEl.textContent = 'Resetting environment...';
145
- stepBtn.disabled = true;
 
146
 
147
- try {
148
- const resp = await fetch(API_BASE + '/reset?difficulty=' + currentDifficulty, {
149
- method: 'POST',
150
- headers: { 'Content-Type': 'application/json' }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  });
152
 
153
- const data = await resp.json();
154
- episodeId = data.episode_id;
155
- stepCount = 0;
156
- episodeReward = 0;
 
 
 
 
157
 
158
- const obs = data.observation;
159
- statusEl.className = 'status ready';
160
- statusEl.textContent = `Episode: ${episodeId.substring(0, 12)}... | ${obs.learning_context.difficulty.toUpperCase()} | Steps: 0/${currentDifficulty === 'easy' ? 50 : currentDifficulty === 'hard' ? 100 : 75}`;
 
161
 
162
- resultEl.innerHTML = JSON.stringify({
163
- "Episode Started": {
164
- episode_id: episodeId,
165
- difficulty: obs.learning_context.difficulty,
166
- topic: obs.learning_context.topic,
167
- multimodal_fused: obs.multimodal_fused,
168
- prediction_window: obs.prediction_window,
169
- available_interventions: obs.available_interventions,
170
- agent_state: { epsilon: 1.0 }
171
- }
172
- }, null, 2);
173
 
174
- metricsEl.innerHTML = '';
175
- stepBtn.disabled = false;
176
 
177
- } catch (e) {
178
- statusEl.className = 'status error';
179
- statusEl.textContent = 'Error: ' + e.message;
180
  }
 
 
181
  }
182
 
183
- async function step() {
184
- if (!episodeId) return;
 
 
 
 
 
 
 
185
 
186
- const predicted = parseFloat(confusionSlider.value);
187
- const intervention = document.getElementById('intervention').value;
188
 
189
- statusEl.className = 'status loading';
190
- statusEl.textContent = 'Processing step...';
191
 
 
 
 
 
 
 
 
 
 
 
192
  try {
193
- const resp = await fetch(API_BASE + '/step', {
194
- method: 'POST',
195
- headers: { 'Content-Type': 'application/json' },
196
- body: JSON.stringify({
197
- episode_id: episodeId,
198
- action_type: intervention ? 'trigger_intervention' : 'predict_confusion',
199
- predicted_confusion: predicted,
200
- intervention_type: intervention || null,
201
- intervention_intensity: intervention ? 0.7 : null
202
- })
203
- });
204
-
205
  const data = await resp.json();
206
- stepCount++;
207
-
208
- if (data.error) {
209
- statusEl.className = 'status error';
210
- statusEl.textContent = data.error;
211
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
 
 
 
213
 
214
- const reward = data.reward;
215
- const obs = data.observation;
216
- episodeReward += reward.total;
217
-
218
- if (data.done) {
219
- const grader = data.info.grader_result;
220
- statusEl.className = grader.passed ? 'status ready' : 'status error';
221
- statusEl.textContent = grader.passed ? 'EPISODE PASSED!' : 'Episode Ended';
222
-
223
- resultEl.innerHTML = JSON.stringify({
224
- "Episode Complete": {
225
- total_steps: stepCount,
226
- total_reward: episodeReward.toFixed(3),
227
- grader_result: grader
228
- }
229
- }, null, 2);
230
-
231
- metricsEl.innerHTML = `
232
- <div class="metric" style="font-size:28px;${grader.passed ? '' : 'background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);'}">${grader.passed ? 'PASSED' : 'FAILED'}</div>
233
- <div class="metric">Score: ${grader.score.toFixed(3)}</div>
234
- <div class="metric">MAE: ${grader.metrics.mae.toFixed(3)}</div>
235
- <div class="metric">Early Detection: ${(grader.metrics.early_detection_rate * 100).toFixed(0)}%</div>
236
- <div class="metric">Interventions: ${grader.metrics.total_interventions}</div>
237
- `;
238
-
239
- stepBtn.disabled = true;
240
- } else {
241
- statusEl.className = 'status ready';
242
- statusEl.textContent = `Episode: ${episodeId.substring(0, 12)}... | Steps: ${stepCount}/${currentDifficulty === 'easy' ? 50 : currentDifficulty === 'hard' ? 100 : 75} | Reward: ${episodeReward.toFixed(2)}`;
243
-
244
- resultEl.innerHTML = JSON.stringify({
245
- step: stepCount,
246
- reward: reward.total.toFixed(3),
247
- ground_truth: reward.metadata.ground_truth.toFixed(3),
248
- prediction_error: reward.metadata.prediction_error.toFixed(3),
249
- prediction: predicted.toFixed(2),
250
- intervention: intervention || null,
251
- learner_state: obs.learner_state
252
- }, null, 2);
253
-
254
- metricsEl.innerHTML = `
255
- <div class="metric">Step: ${stepCount}</div>
256
- <div class="metric">Reward: ${reward.total.toFixed(3)}</div>
257
- <div class="metric">Error: ${reward.metadata.prediction_error.toFixed(3)}</div>
258
- `;
259
- }
260
 
261
- } catch (e) {
262
- statusEl.className = 'status error';
263
- statusEl.textContent = 'Error: ' + e.message;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  }
266
 
267
- checkAPI();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  </script>
269
  </body>
270
  </html>
 
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
+ <title>ContextFlow - Learning Tracker</title>
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js" crossorigin="anonymous"></script>
7
+ <script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script>
8
  <style>
9
+ * { box-sizing: border-box; margin: 0; padding: 0; }
10
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); min-height: 100vh; color: white; }
11
+
12
+ .screen { display: none; padding: 20px; max-width: 1200px; margin: 0 auto; }
13
+ .screen.active { display: block; }
14
+
15
+ /* Consent Screen */
16
+ #consent-screen { text-align: center; padding-top: 50px; }
17
+ .consent-box { background: rgba(255,255,255,0.1); border-radius: 20px; padding: 40px; max-width: 600px; margin: 0 auto; backdrop-filter: blur(10px); }
18
+ .consent-box h1 { color: #00d4ff; margin-bottom: 20px; }
19
+ .consent-box p { margin: 15px 0; line-height: 1.6; color: #ccc; }
20
+ .permissions { text-align: left; margin: 30px 0; }
21
+ .permission { display: flex; align-items: center; gap: 15px; padding: 15px; background: rgba(255,255,255,0.05); border-radius: 10px; margin: 10px 0; }
22
+ .permission-icon { font-size: 30px; }
23
+ .permission h3 { color: #00d4ff; }
24
+ .permission p { color: #888; font-size: 14px; }
25
+ .consent-btn { background: linear-gradient(135deg, #00d4ff 0%, #00b4d8 100%); color: #1a1a2e; border: none; padding: 20px 50px; border-radius: 30px; font-size: 18px; font-weight: bold; cursor: pointer; margin: 10px; transition: transform 0.3s; }
26
+ .consent-btn:hover { transform: scale(1.05); }
27
+ .consent-btn.decline { background: rgba(255,255,255,0.2); color: white; }
28
+
29
+ /* Dashboard */
30
+ .dashboard { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px; }
31
+ .card { background: rgba(255,255,255,0.05); border-radius: 15px; padding: 20px; }
32
+ .card h2 { color: #00d4ff; border-bottom: 2px solid #00d4ff; padding-bottom: 10px; margin-bottom: 15px; font-size: 16px; }
33
+
34
+ /* Camera */
35
+ .camera-container { position: relative; background: #000; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; }
36
+ #video { width: 100%; height: 100%; object-fit: cover; transform: scaleX(-1); }
37
+ #canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; transform: scaleX(-1); }
38
+ .camera-off { display: flex; align-items: center; justify-content: center; height: 100%; color: #666; font-size: 20px; }
39
+
40
+ /* Signals */
41
+ .signal-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }
42
+ .signal { text-align: center; padding: 15px; background: rgba(255,255,255,0.05); border-radius: 10px; }
43
+ .signal-value { font-size: 28px; font-weight: bold; color: #00d4ff; }
44
+ .signal-label { font-size: 12px; color: #888; margin-top: 5px; }
45
+
46
+ /* Confusion Meter */
47
+ .confusion-meter { text-align: center; padding: 30px; }
48
+ .confusion-circle { width: 200px; height: 200px; border-radius: 50%; background: conic-gradient(#00d4ff var(--progress, 0%), rgba(255,255,255,0.1) 0%); display: flex; align-items: center; justify-content: center; margin: 0 auto; transition: all 0.5s; }
49
+ .confusion-inner { width: 160px; height: 160px; border-radius: 50%; background: #1a1a2e; display: flex; align-items: center; justify-content: center; flex-direction: column; }
50
+ .confusion-value { font-size: 48px; font-weight: bold; color: #00d4ff; }
51
+ .confusion-label { font-size: 14px; color: #888; }
52
+ .confusion-level { margin-top: 15px; font-size: 20px; font-weight: bold; padding: 10px 20px; border-radius: 20px; display: inline-block; }
53
+ .level-low { background: #10b981; }
54
+ .level-medium { background: #f59e0b; }
55
+ .level-high { background: #ef4444; }
56
+
57
+ /* Intervention */
58
+ .intervention-box { padding: 20px; text-align: center; }
59
+ .intervention-btn { background: linear-gradient(135deg, #00d4ff 0%, #00b4d8 100%); color: #1a1a2e; border: none; padding: 15px 30px; border-radius: 25px; font-size: 16px; font-weight: bold; cursor: pointer; margin: 10px; }
60
+ .intervention-btn:disabled { opacity: 0.5; cursor: not-allowed; }
61
+ .suggested-intervention { background: rgba(0,212,255,0.2); padding: 15px; border-radius: 10px; margin-top: 15px; }
62
+
63
+ /* Behavioral Stats */
64
+ .behavior-stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
65
+ .stat { background: rgba(255,255,255,0.05); padding: 15px; border-radius: 10px; text-align: center; }
66
+ .stat-value { font-size: 24px; font-weight: bold; color: #00d4ff; }
67
+ .stat-label { font-size: 12px; color: #888; }
68
+
69
+ /* Status Bar */
70
+ .status-bar { display: flex; justify-content: space-between; align-items: center; padding: 15px 20px; background: rgba(0,0,0,0.3); border-radius: 10px; margin-bottom: 20px; }
71
+ .status-item { display: flex; align-items: center; gap: 10px; }
72
+ .status-dot { width: 10px; height: 10px; border-radius: 50%; background: #10b981; }
73
+ .status-dot.off { background: #ef4444; }
74
+
75
+ /* Header */
76
+ .header { text-align: center; padding: 20px; }
77
+ .header h1 { color: #00d4ff; font-size: 28px; }
78
+ .header p { color: #888; margin-top: 5px; }
79
+
80
+ /* Toggle */
81
+ .toggle-container { display: flex; align-items: center; justify-content: space-between; padding: 15px; background: rgba(255,255,255,0.05); border-radius: 10px; margin: 10px 0; }
82
+ .toggle { position: relative; width: 50px; height: 26px; }
83
+ .toggle input { opacity: 0; width: 0; height: 0; }
84
+ .toggle-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background: #666; border-radius: 26px; transition: 0.3s; }
85
+ .toggle-slider:before { position: absolute; content: ""; height: 20px; width: 20px; left: 3px; bottom: 3px; background: white; border-radius: 50%; transition: 0.3s; }
86
+ .toggle input:checked + .toggle-slider { background: #00d4ff; }
87
+ .toggle input:checked + .toggle-slider:before { transform: translateX(24px); }
88
+
89
+ /* Privacy Controls */
90
+ .privacy-controls { margin-top: 20px; }
91
+
92
+ @media (max-width: 768px) {
93
+ .dashboard { grid-template-columns: 1fr; }
94
+ .signal-grid { grid-template-columns: repeat(2, 1fr); }
95
+ }
96
  </style>
97
  </head>
98
  <body>
99
+ <!-- Consent Screen -->
100
+ <div id="consent-screen" class="screen active">
101
+ <div class="consent-box">
102
+ <h1>ContextFlow Learning Tracker</h1>
103
+ <p>Help us understand your learning patterns to provide better support!</p>
104
+
105
+ <div class="permissions">
106
+ <div class="permission">
107
+ <span class="permission-icon">📷</span>
108
+ <div>
109
+ <h3>Camera Access</h3>
110
+ <p>We analyze hand gestures to detect confusion signals. Your face is automatically blurred for privacy.</p>
111
+ </div>
112
+ </div>
113
+ <div class="permission">
114
+ <span class="permission-icon">🖱️</span>
115
+ <div>
116
+ <h3>Browser Behavior</h3>
117
+ <p>We track scroll patterns, clicks, and typing to understand engagement levels.</p>
118
+ </div>
119
+ </div>
120
+ <div class="permission">
121
+ <span class="permission-icon">🎤</span>
122
+ <div>
123
+ <h3>Microphone (Optional)</h3>
124
+ <p>Analyze voice patterns for confusion detection. Can be disabled anytime.</p>
125
+ </div>
126
+ </div>
127
  </div>
128
+
129
+ <p style="color: #00d4ff; margin: 20px 0;">
130
+ <strong>Privacy First:</strong> All processing happens locally. No data is sent to servers without your explicit consent.
131
+ </p>
132
+
133
+ <button class="consent-btn" onclick="startTracking()">I Agree - Start Tracking</button>
134
+ <button class="consent-btn decline" onclick="startSimulation()">Continue Without Tracking</button>
135
+
136
+ <p style="margin-top: 30px; color: #666; font-size: 12px;">
137
+ By continuing, you agree to our Privacy Policy. You can revoke permissions anytime.
138
+ </p>
139
+ </div>
140
+ </div>
141
+
142
+ <!-- Main Dashboard -->
143
+ <div id="dashboard" class="screen">
144
+ <div class="header">
145
+ <h1>ContextFlow Learning Tracker</h1>
146
+ <p>Real-time Confusion Detection with RL Agents</p>
147
  </div>
148
 
149
+ <div class="status-bar">
150
+ <div class="status-item">
151
+ <div class="status-dot" id="camera-status"></div>
152
+ <span>Camera</span>
 
 
153
  </div>
154
+ <div class="status-item">
155
+ <div class="status-dot" id="tracking-status"></div>
156
+ <span>Tracking</span>
 
 
 
 
 
 
 
 
 
 
157
  </div>
158
+ <div class="status-item">
159
+ <span id="session-time">00:00</span>
160
+ </div>
161
+ <button class="consent-btn" style="padding: 10px 20px; font-size: 14px;" onclick="stopTracking()">Stop</button>
 
 
 
 
 
 
 
162
  </div>
163
 
164
+ <div class="dashboard">
165
+ <!-- Camera Feed -->
166
+ <div class="card">
167
+ <h2>Hand Tracking</h2>
168
+ <div class="camera-container">
169
+ <video id="video" playsinline></video>
170
+ <canvas id="canvas"></canvas>
171
+ <div id="camera-off" class="camera-off" style="display:none;">Camera Off</div>
172
+ </div>
173
+ <div class="privacy-controls">
174
+ <div class="toggle-container">
175
+ <span>Enable Camera</span>
176
+ <label class="toggle">
177
+ <input type="checkbox" id="camera-toggle" checked onchange="toggleCamera()">
178
+ <span class="toggle-slider"></span>
179
+ </label>
180
+ </div>
181
+ <div class="toggle-container">
182
+ <span>Face Blur</span>
183
+ <label class="toggle">
184
+ <input type="checkbox" id="face-blur-toggle" checked>
185
+ <span class="toggle-slider"></span>
186
+ </label>
187
+ </div>
188
+ </div>
189
+ </div>
190
+
191
+ <!-- Confusion Detection -->
192
+ <div class="card">
193
+ <h2>Confusion Level</h2>
194
+ <div class="confusion-meter">
195
+ <div class="confusion-circle" id="confusion-circle">
196
+ <div class="confusion-inner">
197
+ <span class="confusion-value" id="confusion-value">0%</span>
198
+ <span class="confusion-label">Confusion</span>
199
+ </div>
200
+ </div>
201
+ <div id="confusion-level" class="confusion-level level-low">ENGAGED</div>
202
+ </div>
203
+ <div class="suggested-intervention" id="intervention-suggestion" style="display:none;">
204
+ <strong>Suggested Intervention:</strong>
205
+ <span id="suggested-intervention-text">Take a short break</span>
206
+ </div>
207
+ <div class="intervention-box">
208
+ <button class="intervention-btn" onclick="triggerIntervention()">Trigger Intervention</button>
209
+ </div>
210
+ </div>
211
+
212
+ <!-- Multi-Modal Signals -->
213
+ <div class="card">
214
+ <h2>Signal Analysis</h2>
215
+ <div class="signal-grid">
216
+ <div class="signal">
217
+ <div class="signal-value" id="gesture-signal">--</div>
218
+ <div class="signal-label">Gesture</div>
219
+ </div>
220
+ <div class="signal">
221
+ <div class="signal-value" id="behavioral-signal">--</div>
222
+ <div class="signal-label">Behavioral</div>
223
+ </div>
224
+ <div class="signal">
225
+ <div class="signal-value" id="temporal-signal">--</div>
226
+ <div class="signal-label">Temporal</div>
227
+ </div>
228
+ <div class="signal">
229
+ <div class="signal-value" id="scroll-signal">--</div>
230
+ <div class="signal-label">Scroll Rate</div>
231
+ </div>
232
+ <div class="signal">
233
+ <div class="signal-value" id="click-signal">--</div>
234
+ <div class="signal-label">Click Rate</div>
235
+ </div>
236
+ <div class="signal">
237
+ <div class="signal-value" id="focus-signal">--</div>
238
+ <div class="signal-label">Focus</div>
239
+ </div>
240
+ </div>
241
+ </div>
242
+
243
+ <!-- Behavioral Stats -->
244
+ <div class="card">
245
+ <h2>Session Statistics</h2>
246
+ <div class="behavior-stats">
247
+ <div class="stat">
248
+ <div class="stat-value" id="total-scrolls">0</div>
249
+ <div class="stat-label">Scrolls</div>
250
+ </div>
251
+ <div class="stat">
252
+ <div class="stat-value" id="total-clicks">0</div>
253
+ <div class="stat-label">Clicks</div>
254
+ </div>
255
+ <div class="stat">
256
+ <div class="stat-value" id="avg-time">0s</div>
257
+ <div class="stat-label">Avg. Time/Element</div>
258
+ </div>
259
+ <div class="stat">
260
+ <div class="stat-value" id="reversal-count">0</div>
261
+ <div class="stat-label">Scroll Reversals</div>
262
+ </div>
263
  </div>
264
+ <div style="margin-top: 20px; padding: 15px; background: rgba(0,212,255,0.1); border-radius: 10px;">
265
+ <strong style="color: #00d4ff;">Active Agents:</strong>
266
+ <div style="margin-top: 10px; font-size: 14px; color: #ccc;">
267
+ <div>• RL Doubt Predictor</div>
268
+ <div>• Behavioral Analyzer</div>
269
+ <div>• Gesture Recognizer</div>
270
+ <div>• Knowledge Graph</div>
271
+ </div>
272
  </div>
273
+ </div>
274
+ </div>
275
+ </div>
276
+
277
+ <!-- Simulation Mode -->
278
+ <div id="simulation" class="screen">
279
+ <div class="header">
280
+ <h1>ContextFlow Simulation Mode</h1>
281
+ <p>Predict confusion without camera access</p>
282
+ </div>
283
+ <div class="dashboard">
284
+ <div class="card">
285
+ <h2>Manual Signal Input</h2>
286
+ <div style="padding: 20px;">
287
+ <label style="display: block; margin: 15px 0;">Frustration Level (0-10)</label>
288
+ <input type="range" id="frustration-input" min="0" max="10" value="5" style="width: 100%;" oninput="updateSimulation()">
289
+ <span id="frustration-value">5</span>
290
+
291
+ <label style="display: block; margin: 15px 0;">Time Spent on Content (minutes)</label>
292
+ <input type="range" id="time-input" min="1" max="60" value="10" style="width: 100%;" oninput="updateSimulation()">
293
+ <span id="time-value">10</span>
294
+
295
+ <label style="display: block; margin: 15px 0;">Content Difficulty</label>
296
+ <select id="difficulty-input" onchange="updateSimulation()">
297
+ <option value="easy">Easy</option>
298
+ <option value="medium" selected>Medium</option>
299
+ <option value="hard">Hard</option>
300
+ </select>
301
  </div>
302
+ </div>
303
+ <div class="card">
304
+ <h2>Confusion Prediction</h2>
305
+ <div class="confusion-meter">
306
+ <div class="confusion-circle" id="sim-confusion-circle">
307
+ <div class="confusion-inner">
308
+ <span class="confusion-value" id="sim-confusion-value">50%</span>
309
+ <span class="confusion-label">Confusion</span>
310
+ </div>
311
+ </div>
312
+ <div id="sim-confusion-level" class="confusion-level level-medium">MODERATE</div>
313
  </div>
314
  </div>
315
  </div>
316
  </div>
317
 
318
  <script>
319
+ // State
320
+ const state = {
321
+ tracking: false,
322
+ cameraEnabled: true,
323
+ sessionStart: null,
324
+ gestures: [],
325
+ scrolls: 0,
326
+ clicks: 0,
327
+ scrollReversals: 0,
328
+ lastScrollY: 0,
329
+ confusionHistory: [],
330
+ signals: {
331
+ gesture: 0.3,
332
+ behavioral: 0.3,
333
+ temporal: 0.3,
334
+ scroll: 0.5,
335
+ click: 0.5,
336
+ focus: 1.0
337
+ }
338
  };
339
 
340
+ // API Configuration
341
+ const API_BASE = 'https://namish10-contextflow-env-api.hf.space';
342
+ let sessionId = null;
343
+ let hands = null;
344
+ let camera = null;
345
+
346
+ // Initialize MediaPipe Hands
347
+ function initHandTracking() {
348
+ hands = new Hands({
349
+ locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`
350
+ });
351
+
352
+ hands.setOptions({
353
+ maxNumHands: 1,
354
+ modelComplexity: 1,
355
+ minDetectionConfidence: 0.7,
356
+ minTrackingConfidence: 0.5
357
+ });
358
+
359
+ hands.onResults(onHandResults);
360
  }
361
 
362
+ function onHandResults(results) {
363
+ const canvas = document.getElementById('canvas');
364
+ const ctx = canvas.getContext('2d');
365
+ canvas.width = video.videoWidth;
366
+ canvas.height = video.videoHeight;
367
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
368
 
369
+ let gestureConfusion = 0.3;
370
+
371
+ if (results.multiHandLandmarks && results.multiHandLandmarks.length > 0) {
372
+ const landmarks = results.multiHandLandmarks[0];
373
+
374
+ // Draw hand landmarks
375
+ ctx.fillStyle = '#00d4ff';
376
+ ctx.strokeStyle = '#00d4ff';
377
+ ctx.lineWidth = 2;
378
+
379
+ // Draw connections
380
+ const connections = [
381
+ [0, 1], [1, 2], [2, 3], [3, 4],
382
+ [0, 5], [5, 6], [6, 7], [7, 8],
383
+ [5, 9], [9, 10], [10, 11], [11, 12],
384
+ [9, 13], [13, 14], [14, 15], [15, 16],
385
+ [13, 17], [17, 18], [18, 19], [19, 20],
386
+ [0, 17]
387
+ ];
388
+
389
+ connections.forEach(([i, j]) => {
390
+ ctx.beginPath();
391
+ ctx.moveTo(landmarks[i].x * canvas.width, landmarks[i].y * canvas.height);
392
+ ctx.lineTo(landmarks[j].x * canvas.width, landmarks[j].y * canvas.height);
393
+ ctx.stroke();
394
  });
395
 
396
+ // Calculate gesture confusion indicators
397
+ const fingerTips = [4, 8, 12, 16, 20];
398
+ let extendedFingers = 0;
399
+ fingerTips.forEach((tip, idx) => {
400
+ const tipY = landmarks[tip].y;
401
+ const baseY = landmarks[tip - 2].y;
402
+ if (tipY < baseY) extendedFingers++;
403
+ });
404
 
405
+ // Head scratch gesture (confusion indicator)
406
+ const thumbTip = landmarks[4];
407
+ const indexTip = landmarks[8];
408
+ const thumbIndexDist = Math.hypot(thumbTip.x - indexTip.x, thumbTip.y - indexTip.y);
409
 
410
+ if (thumbIndexDist < 0.1) {
411
+ gestureConfusion = 0.8; // Thumb touching index = confusion
412
+ } else if (extendedFingers <= 2) {
413
+ gestureConfusion = 0.6; // Few fingers extended
414
+ } else if (extendedFingers >= 4) {
415
+ gestureConfusion = 0.2; // Many fingers = engaged
416
+ }
 
 
 
 
417
 
418
+ state.gestures.push(gestureConfusion);
419
+ if (state.gestures.length > 30) state.gestures.shift();
420
 
421
+ document.getElementById('gesture-signal').textContent = (gestureConfusion * 100).toFixed(0) + '%';
 
 
422
  }
423
+
424
+ updateConfusion();
425
  }
426
 
427
+ async function startTracking() {
428
+ // Hide consent, show dashboard
429
+ document.getElementById('consent-screen').classList.remove('active');
430
+ document.getElementById('dashboard').classList.add('active');
431
+
432
+ // Start session
433
+ state.tracking = true;
434
+ state.sessionStart = Date.now();
435
+ sessionId = 'session_' + Date.now();
436
 
437
+ // Update status
438
+ document.getElementById('tracking-status').classList.remove('off');
439
 
440
+ // Initialize hand tracking
441
+ initHandTracking();
442
 
443
+ // Start camera
444
+ startCamera();
445
+
446
+ // Start behavioral tracking
447
+ initBehavioralTracking();
448
+
449
+ // Start session timer
450
+ setInterval(updateSessionTime, 1000);
451
+
452
+ // Create session on backend
453
  try {
454
+ const resp = await fetch(API_BASE + '/reset?difficulty=medium', { method: 'POST' });
 
 
 
 
 
 
 
 
 
 
 
455
  const data = await resp.json();
456
+ sessionId = data.episode_id;
457
+ } catch (e) {
458
+ console.log('API not available, running locally');
459
+ }
460
+ }
461
+
462
+ async function startCamera() {
463
+ const video = document.getElementById('video');
464
+ const cameraStatus = document.getElementById('camera-status');
465
+
466
+ try {
467
+ const stream = await navigator.mediaDevices.getUserMedia({
468
+ video: { facingMode: 'user', width: 640, height: 480 }
469
+ });
470
+ video.srcObject = stream;
471
+ video.onloadedmetadata = () => {
472
+ video.play();
473
+ hands.initialize();
474
+ camera = new Camera(video, {
475
+ onFrame: async () => {
476
+ await hands.send({ image: video });
477
+ },
478
+ width: 640,
479
+ height: 480
480
+ });
481
+ camera.start();
482
+ };
483
+ cameraStatus.classList.remove('off');
484
+ document.getElementById('camera-off').style.display = 'none';
485
+ } catch (e) {
486
+ console.log('Camera access denied:', e);
487
+ cameraStatus.classList.add('off');
488
+ document.getElementById('camera-off').style.display = 'flex';
489
+ }
490
+ }
491
+
492
+ function toggleCamera() {
493
+ state.cameraEnabled = document.getElementById('camera-toggle').checked;
494
+ const video = document.getElementById('video');
495
+ const canvas = document.getElementById('canvas');
496
+
497
+ if (state.cameraEnabled && camera) {
498
+ camera.start();
499
+ video.style.display = 'block';
500
+ } else {
501
+ if (camera) camera.stop();
502
+ video.srcObject = null;
503
+ video.style.display = 'none';
504
+ canvas.style.display = 'none';
505
+ }
506
+
507
+ document.getElementById('camera-status').classList.toggle('off', !state.cameraEnabled);
508
+ }
509
+
510
+ function initBehavioralTracking() {
511
+ // Scroll tracking
512
+ window.addEventListener('scroll', () => {
513
+ const currentY = window.scrollY;
514
+ if (currentY < state.lastScrollY) {
515
+ state.scrollReversals++;
516
+ document.getElementById('reversal-count').textContent = state.scrollReversals;
517
  }
518
+ state.scrolls++;
519
+ state.lastScrollY = currentY;
520
+ document.getElementById('total-scrolls').textContent = state.scrolls;
521
 
522
+ // Scroll-based confusion
523
+ const scrollConfusion = Math.min(1, state.scrollReversals / 20);
524
+ state.signals.scroll = scrollConfusion;
525
+ document.getElementById('scroll-signal').textContent = scrollConfusion.toFixed(1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
 
527
+ updateConfusion();
528
+ });
529
+
530
+ // Click tracking
531
+ window.addEventListener('click', () => {
532
+ state.clicks++;
533
+ document.getElementById('total-clicks').textContent = state.clicks;
534
+ });
535
+
536
+ // Focus tracking
537
+ window.addEventListener('blur', () => {
538
+ state.signals.focus = 0.3;
539
+ document.getElementById('focus-signal').textContent = '30%';
540
+ updateConfusion();
541
+ });
542
+
543
+ window.addEventListener('focus', () => {
544
+ state.signals.focus = 1.0;
545
+ document.getElementById('focus-signal').textContent = '100%';
546
+ updateConfusion();
547
+ });
548
+ }
549
+
550
+ function updateConfusion() {
551
+ // Weighted fusion of signals
552
+ const weights = {
553
+ gesture: 0.25,
554
+ behavioral: 0.25,
555
+ temporal: 0.20,
556
+ scroll: 0.15,
557
+ focus: 0.15
558
+ };
559
+
560
+ const avgGesture = state.gestures.length > 0
561
+ ? state.gestures.reduce((a, b) => a + b, 0) / state.gestures.length
562
+ : 0.3;
563
+
564
+ const behavioralConfusion = (state.signals.scroll * 0.5 + (1 - state.signals.focus) * 0.5);
565
+ const temporalConfusion = Math.min(1, (Date.now() - state.sessionStart) / 300000); // Increases over 5 min
566
+
567
+ const confusion =
568
+ avgGesture * weights.gesture +
569
+ behavioralConfusion * weights.behavioral +
570
+ temporalConfusion * weights.temporal +
571
+ state.signals.scroll * weights.scroll +
572
+ (1 - state.signals.focus) * weights.focus;
573
+
574
+ state.confusionHistory.push(confusion);
575
+ if (state.confusionHistory.length > 100) state.confusionHistory.shift();
576
+
577
+ // Update UI
578
+ const confusionPercent = (confusion * 100).toFixed(0);
579
+ document.getElementById('confusion-value').textContent = confusionPercent + '%';
580
+ document.getElementById('confusion-circle').style.setProperty('--progress', confusionPercent + '%');
581
+
582
+ // Update confusion level
583
+ const levelEl = document.getElementById('confusion-level');
584
+ if (confusion < 0.3) {
585
+ levelEl.textContent = 'ENGAGED';
586
+ levelEl.className = 'confusion-level level-low';
587
+ document.getElementById('intervention-suggestion').style.display = 'none';
588
+ } else if (confusion < 0.6) {
589
+ levelEl.textContent = 'MODERATE';
590
+ levelEl.className = 'confusion-level level-medium';
591
+ document.getElementById('suggested-intervention-text').textContent = 'Consider a hint';
592
+ document.getElementById('intervention-suggestion').style.display = 'block';
593
+ } else {
594
+ levelEl.textContent = 'CONFUSED';
595
+ levelEl.className = 'confusion-level level-high';
596
+ document.getElementById('suggested-intervention-text').textContent = 'Offer simplified explanation';
597
+ document.getElementById('intervention-suggestion').style.display = 'block';
598
  }
599
+
600
+ // Update signals display
601
+ document.getElementById('behavioral-signal').textContent = (behavioralConfusion * 100).toFixed(0) + '%';
602
+ document.getElementById('temporal-signal').textContent = (temporalConfusion * 100).toFixed(0) + '%';
603
+ }
604
+
605
+ function triggerIntervention() {
606
+ const interventions = ['hint', 'simplify', 'breakdown', 'example', 'scaffold'];
607
+ const intervention = interventions[Math.floor(Math.random() * interventions.length)];
608
+ alert('Intervention triggered: ' + intervention);
609
+ }
610
+
611
+ function updateSessionTime() {
612
+ if (!state.sessionStart) return;
613
+ const elapsed = Math.floor((Date.now() - state.sessionStart) / 1000);
614
+ const mins = Math.floor(elapsed / 60).toString().padStart(2, '0');
615
+ const secs = (elapsed % 60).toString().padStart(2, '0');
616
+ document.getElementById('session-time').textContent = `${mins}:${secs}`;
617
+ }
618
+
619
+ function stopTracking() {
620
+ state.tracking = false;
621
+ if (camera) camera.stop();
622
+ document.getElementById('tracking-status').classList.add('off');
623
+ document.getElementById('dashboard').classList.remove('active');
624
+ document.getElementById('consent-screen').classList.add('active');
625
  }
626
 
627
+ function startSimulation() {
628
+ document.getElementById('consent-screen').classList.remove('active');
629
+ document.getElementById('simulation').classList.add('active');
630
+ }
631
+
632
+ function updateSimulation() {
633
+ const frustration = document.getElementById('frustration-input').value / 10;
634
+ const time = document.getElementById('time-input').value;
635
+ const difficulty = document.getElementById('difficulty-input').value;
636
+
637
+ document.getElementById('frustration-value').textContent = document.getElementById('frustration-input').value;
638
+ document.getElementById('time-value').textContent = document.getElementById('time-input').value;
639
+
640
+ let confusion = frustration;
641
+ if (time > 30) confusion += 0.2;
642
+ if (difficulty === 'hard') confusion += 0.2;
643
+ confusion = Math.min(1, confusion);
644
+
645
+ const confusionPercent = (confusion * 100).toFixed(0);
646
+ document.getElementById('sim-confusion-value').textContent = confusionPercent + '%';
647
+ document.getElementById('sim-confusion-circle').style.setProperty('--progress', confusionPercent + '%');
648
+
649
+ const levelEl = document.getElementById('sim-confusion-level');
650
+ if (confusion < 0.3) {
651
+ levelEl.textContent = 'LOW';
652
+ levelEl.className = 'confusion-level level-low';
653
+ } else if (confusion < 0.6) {
654
+ levelEl.textContent = 'MODERATE';
655
+ levelEl.className = 'confusion-level level-medium';
656
+ } else {
657
+ levelEl.textContent = 'HIGH';
658
+ levelEl.className = 'confusion-level level-high';
659
+ }
660
+ }
661
  </script>
662
  </body>
663
  </html>