// Temporary visual probe against the isolated signed-out native WebView. Does not write app data // except theme selection, which is restored before exit. Injected CSS lives only in this page. import { writeFile, mkdir } from 'node:fs/promises'; import { join } from 'node:path'; const [port='9345', out='../../verification/theme-matrix/native-css-probe'] = process.argv.slice(2); await mkdir(out,{recursive:true}); const targets=await(await fetch(`http://127.0.0.1:${port}/json/list`)).json(); const x=targets.find(t=>t.type==='page'&&t.url.startsWith('https://x.com/i/jf/onboarding')); const shell=targets.find(t=>t.type==='page'&&t.url.startsWith('http://tauri.localhost')); if(!x||!shell) throw new Error('isolated app is not ready'); function session(t){const ws=new WebSocket(t.webSocketDebuggerUrl);let id=0;const p=new Map();ws.onmessage=e=>{const m=JSON.parse(e.data);p.get(m.id)?.(m);p.delete(m.id)};const ready=new Promise(r=>ws.onopen=r);const send=async(method,params={})=>{await ready;return new Promise((resolve,reject)=>{const n=++id;p.set(n,m=>m.error?reject(new Error(m.error.message)):resolve(m.result));ws.send(JSON.stringify({id:n,method,params}))})};const ev=async expression=>{const r=await send('Runtime.evaluate',{expression,awaitPromise:true,returnByValue:true});if(r.exceptionDetails)throw new Error(r.exceptionDetails.exception?.description??r.exceptionDetails.text);return r.result.value};return{send,ev,close:()=>ws.close()}} const X=session(x),S=session(shell);const invoke=(c,a={})=>S.ev(`window.__TAURI_INTERNALS__.invoke(${JSON.stringify(c)},${JSON.stringify(a)})`); const original=await invoke('theme_get'); const results=[];const wait=ms=>new Promise(r=>setTimeout(r,ms)); try{ for(const preset of ['night-ink','classic','paper','terminal','high-contrast']){ await invoke('theme_set',{theme:{...original,preset,accent:null}});await wait(2200); const state=await X.ev(`(() => {let st=document.getElementById('qa-signedout-probe');if(!st){st=document.createElement('style');st.id='qa-signedout-probe';document.head.append(st)}const root='body:not(:has(main[role="main"])):not(#bt-unused-id):not(#bt-unused-id2):not(#bt-unused-id3):not(#bt-unused-id4):not(#bt-unused-id5)';st.textContent=root+' button:has(.text-text > p.jf-element) > .jf-element.flex, '+root+' .jf-gsi-face {background-color:#f8fafc !important} '+root+' button > .jf-element.flex .text-text > p.jf-element, '+root+' .jf-gsi-face > span.jf-element {color:#0f1419 !important;-webkit-text-fill-color:#0f1419 !important}';const names=['phone','Apple','Google'];return {theme:window.wayBetter.api.theme.current().id,controls:names.map(n=>{const e=n==='Google'?document.querySelector('.jf-gsi-face'):([...document.querySelectorAll('button')].find(b=>b.textContent?.includes('Continue with '+n))) ;const wrap=n==='Google'?e:e?.querySelector(':scope > div.jf-element.flex');const label=n==='Google'?e?.querySelector(':scope > span.jf-element'):e?.querySelector('.text-text > p.jf-element');return {name:n,bg:wrap&&getComputedStyle(wrap).backgroundColor,style:wrap?.getAttribute("style"),match:wrap?.matches(st.sheet.cssRules[0].selectorText.split(",")[0]),color:label&&getComputedStyle(label).color,fill:label&&getComputedStyle(label).webkitTextFillColor,text:label?.textContent}}),styleRules:st.sheet?.cssRules.length??-1}})()`); const shot=await X.send('Page.captureScreenshot',{format:'png',captureBeyondViewport:false}); await writeFile(join(out,`${preset}.png`),Buffer.from(shot.data,'base64')); results.push({preset,...state}); } }finally{await invoke('theme_set',{theme:original}).catch(()=>{});await X.ev(`document.getElementById('qa-signedout-probe')?.remove()`).catch(()=>{});X.close();S.close()} console.log(JSON.stringify(results));