timmy-talking-turd/video/record_demo.mjs

86 lines
5.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { chromium } from 'playwright';
import { mkdir } from 'node:fs/promises';
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
await mkdir('video/raw',{recursive:true});
const browser=await chromium.launch({headless:true});
const context=await browser.newContext({
viewport:{width:405,height:720},
deviceScaleFactor:1,
recordVideo:{dir:'video/raw',size:{width:720,height:1280}},
colorScheme:'light'
});
const page=await context.newPage();
await page.goto('http://127.0.0.1:4173',{waitUntil:'networkidle'});
const now=Date.now();
const seed=Array.from({length:7},(_,i)=>({
id:`demo-${i}`,occurredAt:new Date(now-(7-i)*86400000).toISOString(),
bristolType:[3,4,4,6,4,3,4][i],color:'brown',urgency:i===3?3:1,discomfort:i===3?2:0,
note:['Morning','After coffee','Easy','Travel day','Back on track','Morning','Feeling regular'][i],photoDataUrl:'',
symptoms:{blood:false,blackOrDarkRed:false,severePain:false,vomiting:false,fever:false,cannotPassGas:false}
}));
await page.evaluate(data=>localStorage.setItem('timmy-ledger-v1',JSON.stringify(data)),seed);
await page.reload({waitUntil:'networkidle'});
await page.addStyleTag({content:`
#demo-touch{position:fixed;z-index:9999;width:54px;height:54px;border:4px solid rgba(255,255,255,.95);background:rgba(245,201,91,.5);box-shadow:0 0 0 8px rgba(21,125,120,.25);border-radius:50%;pointer-events:none;transform:translate(-50%,-50%)}
.demo-focus{box-shadow:0 0 0 5px rgba(245,201,91,.95),0 0 0 10px rgba(21,125,120,.3)!important;transition:.2s!important}
`});
async function touch(selector,{click=true,after=700}={}){
const loc=page.locator(selector).first();await loc.scrollIntoViewIfNeeded();const b=await loc.boundingBox();if(!b)throw new Error(`missing ${selector}`);
await page.evaluate(({x,y})=>{document.querySelector('#demo-touch')?.remove();const d=document.createElement('div');d.id='demo-touch';d.style.left=x+'px';d.style.top=y+'px';document.body.append(d);d.animate([{transform:'translate(-50%,-50%) scale(.55)',opacity:.2},{transform:'translate(-50%,-50%) scale(1)',opacity:1},{transform:'translate(-50%,-50%) scale(.72)',opacity:.8}],{duration:520,easing:'ease-out'});setTimeout(()=>d.remove(),650)},{x:b.x+b.width/2,y:b.y+b.height/2});
await sleep(300);if(click)await loc.click();await sleep(after);
}
async function focus(selector,ms=1500){const loc=page.locator(selector).first();await loc.scrollIntoViewIfNeeded();await loc.evaluate(e=>e.classList.add('demo-focus'));await sleep(ms);await loc.evaluate(e=>e.classList.remove('demo-focus')).catch(()=>{});}
// 08: Hero and value.
await sleep(3000);
await page.evaluate(()=>scrollTo({top:260,behavior:'smooth'}));await sleep(2600);
await page.evaluate(()=>scrollTo({top:0,behavior:'smooth'}));await sleep(1400);
// 817: form selection.
await touch('[data-log]');
await sleep(1500);
await touch('[data-type="3"]',{after:600});
await touch('[data-type="4"]',{after:1700});
await touch('#next',{after:900});
// 1729: context and private photo lane.
await touch('#color',{click:false,after:900});
await page.locator('#urgency').fill('2');await focus('#urgency',900);
await page.locator('#discomfort').fill('1');await focus('#discomfort',700);
await page.locator('#note').fill('After lunch — feeling normal');await focus('#note',1200);
await focus('.photo-drop',2500);
await focus('.fine',1300);
await touch('#next',{after:1400});
// 2943: safety boundary.
await sleep(1700);
await touch('[data-symptom="blood"]',{after:900});
await focus('.alert',5200);
await touch('[data-symptom="blood"]',{after:800});
await touch('#save',{after:1700});
// 4351: pattern view and calendar.
await focus('.summary-card',2500);
await touch('[data-view="calendar"]',{after:1500});
await focus('.calendar',3000);
// 5162: Timmy boundary.
await touch('[data-view="timmy"]',{after:1600});
await touch('[data-prompt="pattern"]',{after:1400});
await touch('[data-prompt="food"]',{after:1100});
await focus('#chat',3600);
// 6270: privacy and control.
await touch('[data-view="privacy"]',{after:1400});
await focus('.privacy-list',2600);
await page.evaluate(()=>scrollTo({top:document.body.scrollHeight,behavior:'smooth'}));await sleep(2300);
await focus('.danger-zone',1400);
// 7076: branded close.
await page.evaluate(()=>{
const o=document.createElement('div');o.id='outro';o.innerHTML=`<img src="/assets/timmy.svg"><div class="small">YOUR INTELLIGENT POOPING PAL</div><div class="big">Know your gut.<br>Keep your dignity.</div><div class="pill">TIMMY THE TALKING TURD</div>`;
Object.assign(o.style,{position:'fixed',inset:'0',zIndex:'10000',background:'linear-gradient(145deg,#f7f3ea,#f5c95b)',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',textAlign:'center',color:'#28211d',fontFamily:'DM Sans,system-ui',opacity:'0'});
o.querySelector('img').style.cssText='width:190px;height:190px;filter:drop-shadow(0 16px 18px rgba(63,37,30,.18))';
o.querySelector('.small').style.cssText='font-size:12px;font-weight:800;letter-spacing:.13em;color:#157d78;margin-top:18px';
o.querySelector('.big').style.cssText='font-size:39px;line-height:1.04;font-weight:800;letter-spacing:-1.5px;margin:12px 0 24px';
o.querySelector('.pill').style.cssText='background:#28211d;color:white;padding:14px 20px;border-radius:99px;font-size:12px;font-weight:800;letter-spacing:.08em';
document.body.append(o);o.animate([{opacity:0},{opacity:1}],{duration:700,fill:'forwards'});
});
await sleep(6100);
const video=page.video();await page.close();await video.saveAs('video/timmy-app-demo-raw.webm');await context.close();await browser.close();
console.log('video/timmy-app-demo-raw.webm');