Mona commited on
Commit
090e374
·
1 Parent(s): af1d617

Upload DailyPal Interactive Prototype

Browse files
Files changed (2) hide show
  1. README.md +16 -12
  2. index.html +283 -17
README.md CHANGED
@@ -1,12 +1,16 @@
1
- ---
2
- title: DailyPal Interactive
3
- emoji: 🐨
4
- colorFrom: green
5
- colorTo: gray
6
- sdk: static
7
- pinned: false
8
- license: mit
9
- short_description: DailyPal Interactive Web
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
1
+ # DailyPal • Interactive Prototype (Static HTML)
2
+
3
+ This folder contains a **single-page interactive prototype** (no backend) that runs in any modern browser.
4
+
5
+ ## Files
6
+ - `index.html` — the interactive prototype (clickable toggles, draggable sliders, Apple-style home screen)
7
+ - `README.md` — this file
8
+
9
+ ## Deploy to Hugging Face Spaces (Static HTML Site)
10
+ 1. Create a new Space at https://huggingface.co/spaces → **Static HTML Site**
11
+ 2. Upload `index.html` (and `README.md` optional). Make sure `index.html` is at the **root**.
12
+ 3. Once the build finishes, your live link will look like:
13
+ `https://huggingface.co/spaces/<YourName>/DailyPal-Interactive`
14
+
15
+ ## Local run
16
+ Just open `index.html` in your browser.
index.html CHANGED
@@ -1,19 +1,285 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!doctype html>
2
+ <html lang='en'>
3
+ <meta charset='utf-8'>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
5
+ <title>DailyPal Interactive Prototype</title>
6
+ <style>
7
+ :root { --w:430px; --h:520px; --r:30px; --stroke:#E5E5EA; --bg:#F2F2F7;
8
+ --green:#34C759; --blue:#007AFF; --dark:#000; --sub:#8E8E93; }
9
+ body { background:#111; font-family:SF Pro Display, Helvetica, Arial, sans-serif; display:flex; flex-direction:column; align-items:center; gap:16px; min-height:100vh; margin:0; justify-content:center; padding:16px; }
10
+ .watch svg { border-radius: var(--r); box-shadow:0 20px 60px rgba(0,0,0,.5); max-width:100%; height:auto; }
11
+ #bar { color:#aaa; font-size:12px; text-align:center; }
12
+ .btnlike { cursor:pointer; }
13
+ </style>
14
+ <body>
15
+ <div id='screen' class='watch'></div>
16
+ <div id='bar'>Press 0:Home, 1:Dashboard, 2:Popup, 3:Scheduler, 4:Weekly, 5:Settings</div>
17
+
18
+ <script>
19
+ const W=430, H=520, R=30;
20
+ const homeSVG = `<svg xmlns='http://www.w3.org/2000/svg' width='430' height='520' viewBox='0 0 430 520'>
21
+ <defs>
22
+ <style>
23
+ .time { font: 700 22px "SF Pro Display, Helvetica, Arial, sans-serif"; fill: #FFFFFF; }
24
+ .label { font: 600 12px "SF Pro Display, Helvetica, Arial, sans-serif"; fill: #FFFFFF; }
25
+ </style>
26
+ </defs>
27
+ <rect x='0' y='0' rx='30' ry='30' width='430' height='520' fill='#000000'/>
28
+ <text class='time' x='20' y='38'>9:41</text>
29
+ <g id='icon-dailypal' cursor='pointer'>
30
+ <circle cx='115' cy='210' r='38' fill='#34C759'/>
31
+ <rect x='100' y='195' width='30' height='24' rx='6' fill='#FFFFFF'/>
32
+ <circle cx='132' cy='205' r='5' fill='#34C759'/>
33
+ <text class='label' x='115' y='260' text-anchor='middle'>DailyPal</text>
34
+ </g>
35
+ <g>
36
+ <circle cx='215' cy='180' r='28' fill='#FF3B30'/>
37
+ <circle cx='280' cy='230' r='32' fill='#FF9500'/>
38
+ <circle cx='190' cy='270' r='26' fill='#0A84FF'/>
39
+ <circle cx='260' cy='305' r='24' fill='#BF5AF2'/>
40
+ <circle cx='158' cy='330' r='22' fill='#64D2FF'/>
41
+ <circle cx='320' cy='190' r='22' fill='#FFD60A'/>
42
+ </g>
43
+ </svg>`;
44
+
45
+ function frameStart(title){
46
+ return `
47
+ <svg xmlns='http://www.w3.org/2000/svg' width='${W}' height='${H}' viewBox='0 0 ${W} ${H}'>
48
+ <defs>
49
+ <style>
50
+ .title { font: 700 22px "SF Pro Display, Helvetica, Arial, sans-serif"; fill: #000; }
51
+ .section { font: 600 17px "SF Pro Display, Helvetica, Arial, sans-serif"; fill: #000; }
52
+ .body { font: 500 14px "SF Pro Display, Helvetica, Arial, sans-serif"; fill: #000; }
53
+ .note { font: 500 12px "SF Pro Display, Helvetica, Arial, sans-serif"; fill: #8E8E93; }
54
+ .btn { font: 700 16px "SF Pro Display, Helvetica, Arial, sans-serif"; fill: #000; }
55
+ </style>
56
+ </defs>
57
+ <rect x='0' y='0' rx='${R}' ry='${R}' width='${W}' height='${H}' fill='#F2F2F7' stroke='#E5E5EA'/>
58
+ <text class='note' x='22' y='24'>9:41</text>
59
+ <text class='title' x='22' y='44'>${title}</text>
60
+ `;
61
+ }
62
+ function frameEnd(){ return "</svg>"; }
63
+
64
+ function pill(x,y,w,h,label,id,fill='#FFFFFF',stroke='#E5E5EA'){
65
+ return `<g id='${id}' class='btnlike'><rect x='${x}' y='${y}' width='${w}' height='${h}' rx='18' fill='${fill}' stroke='${stroke}'/><text class='btn' x='${x+w/2}' y='${y+h/2+6}' text-anchor='middle'>${label}</text></g>`;
66
+ }
67
+
68
+ function toggle(x,y,on,id){
69
+ const fill = on ? '#34C759' : '#E9E9ED';
70
+ const knob = x + (on ? 48 : 18);
71
+ return `<g id='${id}' class='btnlike' data-on='${on?1:0}'>
72
+ <rect x='${x}' y='${y}' width='66' height='32' rx='16' fill='${fill}' stroke='#E5E5EA'/>
73
+ <circle cx='${knob}' cy='${y+16}' r='13' fill='#FFFFFF' stroke='#E5E5EA'/>
74
+ </g>`;
75
+ }
76
+
77
+ function checkbox(x,y,checked,label,id){
78
+ const mark = checked ? `<path d='M ${x+4} ${y+12} l 6 6 l 10 -12' stroke='#007AFF' stroke-width='2' fill='none'/>` : '';
79
+ return `<g id='${id}' class='btnlike' data-on='${checked?1:0}'>
80
+ <rect x='${x}' y='${y}' width='24' height='24' rx='6' fill='#FFFFFF' stroke='#E5E5EA'/>
81
+ ${mark}
82
+ <text class='body' x='${x+32}' y='${y+17}'>${label}</text>
83
+ </g>`;
84
+ }
85
+
86
+ function slider(x,y,w,label,value,id) {
87
+ const pos = 0.65;
88
+ const knobX = x + w*pos;
89
+ return `<g id='${id}' class='slider' data-x='${x}' data-w='${w}' data-pos='${pos}'>
90
+ <text class='section' x='${x}' y='${y-12}'>${label}</text>
91
+ <rect x='${x}' y='${y}' width='${w}' height='6' rx='3' fill='#E5E7EB'/>
92
+ <circle cx='${knobX}' cy='${y+3}' r='10' fill='#FFFFFF' stroke='#E5E5EA'/>
93
+ <text class='note' id='${id}-val' x='${x+w}' y='${y-12}' text-anchor='end'>${value}</text>
94
+ </g>`;
95
+ }
96
+
97
+ function weeklyBars(x,y,w,h,vals){
98
+ let out='<g>';
99
+ const bw = w/(vals.length*1.6); const gap=bw*0.6;
100
+ for(let i=0;i<vals.length;i++){
101
+ const v=vals[i];
102
+ const color = v>0.75 ? '#B8E6C1' : (v>0.45 ? '#FFDDA6' : '#FFB3A8');
103
+ const bx = x + i*(bw+gap); const bh=v*h; const by=y+(h-bh);
104
+ out += `<rect x='${bx}' y='${by}' width='${bw}' height='${bh}' rx='4' fill='${color}' stroke='#E5E5EA'/>`;
105
+ }
106
+ out+='</g>'; return out;
107
+ }
108
+
109
+ function dashboard(){
110
+ let s=frameStart('Next dose • 9:00 AM');
111
+ s += `<text class='note' x='22' y='66'>2 of 3 done today — Doing well!</text>`;
112
+ function row(y,label,right){
113
+ return `<g>
114
+ <rect x='18' y='${y}' width='${W-36}' height='64' rx='16' fill='#FFFFFF' stroke='#E5E5EA'/>
115
+ <circle cx='40' cy='${y+32}' r='10' fill='#E9F8EF' stroke='#E5E5EA'/>
116
+ <path d='M 36 ${y+32} l 6 6 l 10 -12' stroke='#34C759' stroke-width='2' fill='none'/>
117
+ <text class='section' x='58' y='${y+37}'>${label}</text>
118
+ <text class='note' x='${W-28}' y='${y+37}' text-anchor='end'>${right}</text>
119
+ </g>`;
120
+ }
121
+ s += row(82,'Taken','8:00 AM');
122
+ s += row(154,'Snoozed','12:00 PM');
123
+ s += row(226,'Skipped','6:00 PM');
124
+ s += pill(18,308,W-36,60,'I took it','btn_dashboard_took','#E9F8EF');
125
+ s += `<text class='note' x='22' y='392'>Weekly summary</text>`;
126
+ s += pill(18,402,W-36,44,'Open Settings','btn_dashboard_settings','#FFFFFF');
127
+ s += frameEnd();
128
+ return s;
129
+ }
130
+
131
+ function popup(){
132
+ let s=frameStart('Time to take medication');
133
+ s += `<rect x='22' y='54' width='${W-44}' height='96' rx='16' fill='#FFFFFF' stroke='#E5E5EA'/>
134
+ <circle cx='46' cy='102' r='12' fill='#F2F2F5' stroke='#E5E5EA'/>
135
+ <text class='section' x='70' y='96'>Aspirin</text><text class='note' x='70' y='116'>1 tablet</text>`;
136
+ s += pill(22,162,120,52,'Snooze','btn_popup_snooze','#FFF7CC');
137
+ s += pill(154,162,120,52,'Skip','btn_popup_skip','#FFE9E7','#F0C2C2');
138
+ s += pill(286,162,120,52,'Taken','btn_popup_taken','#E9F8EF','#B9E6C6');
139
+ s += frameEnd(); return s;
140
+ }
141
+
142
+ function scheduler(){
143
+ let s=frameStart('Adaptive Scheduler');
144
+ s += slider(22,78,W-44,'Morning','8:00 AM','sld_morning');
145
+ s += slider(22,132,W-44,'Noon','12:30 PM','sld_noon');
146
+ s += slider(22,186,W-44,'Evening','9:00 PM','sld_evening');
147
+ s += `<text class='section' x='22' y='244'>Quiet Hours 10:00 PM–7:00 AM</text>`;
148
+ s += toggle(W-22-66,224,true,'tgl_quiet');
149
+ s += `<text class='section' x='22' y='292'>Auto-learn from your responses</text>`;
150
+ s += toggle(W-22-66,272,true,'tgl_autolearn');
151
+ s += pill(22,324,180,56,'Save','btn_scheduler_save','#E9F8EF');
152
+ s += pill(W-22-180,324,180,56,'Cancel','btn_scheduler_cancel');
153
+ s += frameEnd(); return s;
154
+ }
155
+
156
+ function weekly(){
157
+ let s=frameStart('Weekly Summary');
158
+ s += weeklyBars(56,70,W-112,150,[0.92,0.6,0.84,0.72,0.35,0.52,0.64]);
159
+ const days=['M','T','W','T','F','S','S'];
160
+ const bw = (W-112)/(days.length*1.6); const gap=bw*0.6;
161
+ for(let i=0;i<days.length;i++){ const x=56+i*(bw+gap)+bw/2; s+=`<text class='note' x='${x}' y='230' text-anchor='middle'>${days[i]}</text>`; }
162
+ s += `<text class='body' x='22' y='260'>You missed 2 doses this week — mostly at night.</text>`;
163
+ s += pill(22,288,W-44,56,'View Details','btn_weekly_view');
164
+ s += frameEnd(); return s;
165
+ }
166
+
167
+ function settings(){
168
+ let s=frameStart('Reminder Settings');
169
+ s += `<text class='section' x='22' y='80'>Alert Style</text>`;
170
+ s += checkbox(22,92,true,'Vibration','chk_vib');
171
+ s += checkbox(22,128,false,'Sound','chk_sound');
172
+ s += checkbox(22,164,false,'Light','chk_light');
173
+ s += `<line x1='22' y1='200' x2='${W-22}' y2='200' stroke='#E5E5EA'/>`;
174
+ s += `<text class='section' x='22' y='232'>Show pop-ups</text>`;
175
+ s += toggle(W-22-66,212,false,'tgl_popups');
176
+ s += `<text class='note' x='22' y='256'>Disable sounds and pop-ups during presentations, etc.</text>`;
177
+ s += `<line x1='22' y1='284' x2='${W-22}' y2='284' stroke='#E5E5EA'/>`;
178
+ s += `<text class='section' x='22' y='316'>Quiet Mode</text>`;
179
+ s += toggle(W-22-66,296,false,'tgl_quietmode');
180
+ s += pill(22,348,W-44,56,'Done','btn_settings_done');
181
+ s += frameEnd(); return s;
182
+ }
183
+
184
+ const container = document.getElementById('screen');
185
+ function render(name){
186
+ const map = {home: homeSVG, dashboard: dashboard(), popup: popup(), scheduler: scheduler(), weekly: weekly(), settings: settings()};
187
+ container.innerHTML = map[name];
188
+ const svg = container.querySelector('svg');
189
+ svg.style.width = '430px'; svg.style.height = '520px';
190
+
191
+ const nav = [
192
+ ['#btn_dashboard_took','weekly'],
193
+ ['#btn_dashboard_settings','settings'],
194
+ ['#btn_popup_taken','dashboard'],
195
+ ['#btn_popup_snooze','dashboard'],
196
+ ['#btn_popup_skip','dashboard'],
197
+ ['#btn_scheduler_save','dashboard'],
198
+ ['#btn_scheduler_cancel','dashboard'],
199
+ ['#btn_weekly_view','dashboard'],
200
+ ['#btn_settings_done','dashboard'],
201
+ ['#icon-dailypal','dashboard']
202
+ ];
203
+ nav.forEach(([sel,target])=>{ const el=svg.querySelector(sel); if(el) el.addEventListener('click',()=>render(target)); });
204
+
205
+ // toggles
206
+ svg.querySelectorAll('[id^=tgl_]').forEach(t=>{
207
+ t.style.cursor='pointer';
208
+ t.addEventListener('click',()=>{
209
+ const rect=t.querySelector('rect');
210
+ const circ=t.querySelector('circle');
211
+ const on = rect.getAttribute('fill')!=='#E9E9ED';
212
+ if(on) {
213
+ rect.setAttribute('fill','#E9E9ED'); circ.setAttribute('cx', parseFloat(rect.getAttribute('x'))+18);
214
+ } else {
215
+ rect.setAttribute('fill','#34C759'); circ.setAttribute('cx', parseFloat(rect.getAttribute('x'))+48);
216
+ }
217
+ });
218
+ });
219
+
220
+ // checkboxes
221
+ [['#chk_vib'],['#chk_sound'],['#chk_light']].forEach(([sel])=>{
222
+ const g = svg.querySelector(sel);
223
+ if(!g) return;
224
+ g.addEventListener('click',()=>{
225
+ const path = g.querySelector('path');
226
+ if(path) path.remove();
227
+ else {
228
+ const x = parseFloat(g.querySelector('rect').getAttribute('x'));
229
+ const y = parseFloat(g.querySelector('rect').getAttribute('y'));
230
+ const p = document.createElementNS('http://www.w3.org/2000/svg','path');
231
+ p.setAttribute('d',`M ${x+4} ${y+12} l 6 6 l 10 -12`);
232
+ p.setAttribute('stroke','#007AFF'); p.setAttribute('stroke-width','2'); p.setAttribute('fill','none');
233
+ g.appendChild(p);
234
+ }
235
+ });
236
+ });
237
+
238
+ // sliders drag
239
+ svg.querySelectorAll('.slider').forEach(g=>{
240
+ const x = parseFloat(g.getAttribute('data-x'));
241
+ const w = parseFloat(g.getAttribute('data-w'));
242
+ const circle = g.querySelector('circle');
243
+ const label = g.querySelector('text.note[id$="-val"]');
244
+ let dragging=false;
245
+
246
+ function updateByClientX(clientX){
247
+ const pt = svg.createSVGPoint(); pt.x = clientX; pt.y = 0;
248
+ const ctm = svg.getScreenCTM().inverse(); const loc = pt.matrixTransform(ctm);
249
+ let nx = Math.max(x, Math.min(x+w, loc.x));
250
+ circle.setAttribute('cx', nx);
251
+ const ratio = (nx - x)/w;
252
+ const minutes = Math.round(7*60 + ratio*(15*60));
253
+ const hr = Math.floor(minutes/60); const min = minutes%60;
254
+ const ampm = hr>=12? 'PM':'AM'; const hr12 = ((hr+11)%12)+1;
255
+ const mm = String(min).padStart(2,'0');
256
+ if(label) label.textContent = `${hr12}:${mm} ${ampm}`;
257
+ }
258
+
259
+ circle.addEventListener('mousedown', (e)=>{ dragging=true; e.preventDefault(); });
260
+ svg.addEventListener('mousemove',(e)=>{ if(dragging) updateByClientX(e.clientX); });
261
+ window.addEventListener('mouseup',()=> dragging=false);
262
+
263
+ // touch support
264
+ circle.addEventListener('touchstart', (e)=>{ dragging=true; e.preventDefault(); });
265
+ svg.addEventListener('touchmove',(e)=>{ if(dragging) updateByClientX(e.touches[0].clientX); });
266
+ window.addEventListener('touchend',()=> dragging=false);
267
+
268
+ // tap track
269
+ g.addEventListener('click',(e)=>{ updateByClientX((e.touches? e.touches[0].clientX : e.clientX)); });
270
+ });
271
+ }
272
+
273
+ render('home');
274
+
275
+ document.addEventListener('keydown',(e)=>{
276
+ if(e.key==='0') render('home');
277
+ if(e.key==='1') render('dashboard');
278
+ if(e.key==='2') render('popup');
279
+ if(e.key==='3') render('scheduler');
280
+ if(e.key==='4') render('weekly');
281
+ if(e.key==='5') render('settings');
282
+ });
283
+ </script>
284
+ </body>
285
  </html>