// Exact text-node/paint ancestry for signed-out X auth buttons in the isolated native WebView. import { writeFile, mkdir } from 'node:fs/promises'; import { join } from 'node:path'; const [port='9345', out='../../verification/theme-matrix/native-auth-dom'] = 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 ['x','night-ink','classic']){ await invoke('theme_set',{theme:{...original,preset,accent:null}});await wait(1200); const dom=await X.ev(`(() => { const path=e=>{const a=[];for(let n=e;n&&n!==document.body;n=n.parentElement){a.push(n.tagName.toLowerCase()+(n.className&&typeof n.className==='string'?'.'+n.className.trim().split(/\\s+/).slice(0,5).join('.'):'')+(n.id?'#'+n.id:''));if(n.matches('button,[role="button"]'))break}return a.join(' < ')}; const paint=e=>{const s=getComputedStyle(e),r=e.getBoundingClientRect();return{tag:e.tagName,classes:typeof e.className==='string'?e.className.slice(0,160):'',path:path(e),rect:[r.x,r.y,r.width,r.height].map(x=>+x.toFixed(1)),color:s.color,bg:s.backgroundColor,fill:s.fill,stroke:s.stroke,textFill:s.webkitTextFillColor,opacity:s.opacity,filter:s.filter,mix:s.mixBlendMode,visibility:s.visibility,display:s.display,clip:s.clipPath,mask:s.maskImage}}; const nodes=[];const w=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT);while(w.nextNode()){const t=w.currentNode.textContent?.trim();if(!t||!/(Continue with|ดำเนินการต่อโดยใช้)/.test(t))continue;const e=w.currentNode.parentElement;const b=e?.closest('button,[role="button"]');if(!b)continue;const chain=[];for(let n=e;n&&n!==document.body;n=n.parentElement){chain.push(paint(n));if(n===b)break}nodes.push({text:t.slice(0,100),chain})} const buttons=[...document.querySelectorAll('button,[role="button"]')].filter(e=>/(Continue with phone|Continue with Apple|Google|ดำเนินการต่อโดยใช้)/.test(e.textContent??'')).slice(0,5).map(e=>({html:e.outerHTML.slice(0,12000),paint:paint(e)})); const iframe=[...document.querySelectorAll('iframe')].filter(e=>e.src.includes('accounts.google.com/gsi/button')).map(e=>{const chain=[];for(let n=e;n&&n!==document.body;n=n.parentElement){chain.push(paint(n));if(n.matches('button,[role="button"]'))break}const r=e.getBoundingClientRect();const center=document.elementFromPoint(r.x+r.width*.65,r.y+r.height*.5);return{chain,atText:center&&paint(center)}}); const googleWrapper=document.querySelector('.jf-gsi-hit')?.parentElement; return {url:location.href,theme:window.wayBetter?.theme.current().preset,nodes,buttons,iframe,googleWrapper:googleWrapper?{html:googleWrapper.outerHTML.slice(0,16000),children:[...googleWrapper.children].map(paint)}:null}; })()`); 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,...dom}); } }finally{await invoke('theme_set',{theme:original}).catch(()=>{});X.close();S.close()} await writeFile(join(out,'dom.json'),JSON.stringify({original,results},null,2)); console.log(JSON.stringify(results.map(r=>({preset:r.preset,nodes:r.nodes.map(n=>({text:n.text,paint:n.chain.slice(0,4).map(c=>({tag:c.tag,path:c.path.slice(0,85),color:c.color,bg:c.bg,textFill:c.textFill,rect:c.rect}))})),iframe:r.iframe.map(f=>({atText:f.atText?.path,chain:f.chain.slice(0,4).map(c=>({tag:c.tag,bg:c.bg,filter:c.filter,opacity:c.opacity,mix:c.mix}))}))})),null,2));