import fs from 'node:fs' import path from 'node:path' import { createHash } from 'node:crypto' import { parse } from '@babel/parser' import traverseModule from '@babel/traverse' import postcss from 'postcss' const traverse=traverseModule.default??traverseModule, evidence=JSON.parse(fs.readFileSync('.tmp/rea-runtime-scripts.json','utf8')),cache=new Map(),checkOnly=process.argv.includes('--check') const HELP_FEEDBACK_EVIDENCE={id:'ev_2309de12a003fca2c863a223cbda5bccad36b3af73e29bc309fdaed94745cf9e',fileSha256:'87511827e6cf8f016c9c073fb654ffcc13520b0770dc2ef997564efc1ffe6ba7',pathLength:534,pathSha256:'d84f185b0d52e3b7eb61c96efc21a34887c213746b12132ddd3595d33e332287'} const hash=(value)=>createHash('sha256').update(value).digest('hex') function source(base){return evidence.items.find((item)=>item.file_path.endsWith(base))?.source.artifact.text} function ast(base){if(!cache.has(base))cache.set(base,parse(source(base),{sourceType:'module'}));return cache.get(base)} const context='contexts-BMeWSUHk.js',staticValue=(node)=>!node?undefined:node.type==='TemplateLiteral'&&!node.expressions.length?node.quasis[0].value.cooked:node.type==='StringLiteral'||node.type==='NumericLiteral'?node.value:undefined function object(node){return Object.fromEntries(node.properties.filter((prop)=>prop.type==='ObjectProperty').map((prop)=>[prop.key.name??prop.key.value,staticValue(prop.value)]))} let tabs,groups traverse(ast(context),{AssignmentExpression(p){if(p.node.left.name==='DC')tabs=p.node.right.elements.map(object);if(p.node.left.name==='cXe')groups=p.node.right.elements.map(object)}}) const labels={};traverse(ast('zh-cn-Dfzn8myY.js'),{ObjectProperty(p){const key=p.node.key.value;if(typeof key==='string'&&/^(settings\.nav\.|settings\.search\.)/.test(key)){const value=staticValue(p.node.value);if(typeof value==='string')labels[key]=value}}}) const names={settings:'Hee',account:'ute',subscriptionUsage:'Ute',appearance:'qre',personalization:'bne',memory:'Na',extensions:'Une',models:'ModelNavIcon',claw:'ClawSettingsIcon',laboratory:'wre',deviceConnections:'MobileConnectIcon',dataManagement:'Kte',keyboardShortcuts:'Qne',buddyApps:'_ne',securityCenter:'Vte',systemPermissions:'$a',softwareConfig:'ro',helpFeedback:'co',getHelp:'One'} function resolve(base,name,depth=0){ if(depth>8)return null let imported,decl traverse(ast(base),{ImportDeclaration(p){for(const spec of p.node.specifiers)if(spec.local.name===name&&spec.type==='ImportSpecifier')imported={base:p.node.source.value.replace('./',''),member:spec.imported.name}},FunctionDeclaration(p){if(p.node.id?.name===name)decl=p},AssignmentExpression(p){if(p.node.left.name===name)decl=p},VariableDeclarator(p){if(p.node.id?.name===name&&p.node.init)decl=p}}) if(imported&&source(imported.base)){let local;traverse(ast(imported.base),{ExportNamedDeclaration(p){for(const spec of p.node.specifiers)if(spec.exported.name===imported.member)local=spec.local.name}});if(local)return resolve(imported.base,local,depth+1)} if(!decl)return null const right=decl.node.right??decl.node.init if(right?.type==='CallExpression'&&typeof staticValue(right.arguments[0])==='string'&&right.arguments[1]?.type==='Identifier') { let nodes;traverse(ast(base),{AssignmentExpression(p){if(p.node.left.name===right.arguments[1].name&&p.node.right.type==='ArrayExpression')nodes=p.node.right.elements}}) if(nodes){let defaults={};traverse(ast('createLucideIcon-1goKJVIH.js'),{AssignmentExpression(p){if(p.node.left.name==='i'&&p.node.right.type==='ObjectExpression')defaults=object(p.node.right)}});return{props:defaults,children:nodes.map((entry)=>({tag:staticValue(entry.elements[0]),props:object(entry.elements[1])}))}} } if(right?.type==='CallExpression'&&right.arguments[0]?.type==='Identifier')return resolve(base,right.arguments[0].name,depth+1) let svg=null,paths=[],next decl.traverse({CallExpression(p){const tag=p.node.arguments[0];if(tag?.type==='Identifier'){next=tag.name;return}const kind=staticValue(tag);if(!['svg','path','circle','rect','line','polyline','polygon','ellipse'].includes(kind))return;const props={};for(const prop of p.node.arguments[1]?.properties??[]){if(prop.type!=='ObjectProperty')continue;const value=staticValue(prop.value);if(value!==undefined)props[prop.key.name??prop.key.value]=value}if(kind==='svg')svg=props;else paths.push({tag:kind,props})}}) if(svg&&paths.length)return{props:svg,children:paths} return next?resolve(base,next,depth+1):null } const icons={};for(const[id,name]of Object.entries(names)){const svg=resolve(context,name);if(svg)icons[id]=svg} const fixedEvidenceFile=path.join('.tmp','rea-css-evidence',HELP_FEEDBACK_EVIDENCE.id+'.json'),fixedEvidenceBytes=fs.readFileSync(fixedEvidenceFile),fixedEvidenceSha=hash(fixedEvidenceBytes) if(fixedEvidenceSha!==HELP_FEEDBACK_EVIDENCE.fileSha256)throw new Error('Fixed settings navigation Evidence changed: '+fixedEvidenceSha) const fixedEvidence=JSON.parse(fixedEvidenceBytes.toString('utf8')) if(fixedEvidence.evidence_id!==HELP_FEEDBACK_EVIDENCE.id)throw new Error('Fixed settings navigation Evidence identity mismatch') const fixedDocument=fixedEvidence.normalized_result.visuals.layout.documents.find((document)=>document.nodes.some((node)=>String(node.attributes?.class??'').split(/\s+/).includes('settings-navigation__group--about'))) if(!fixedDocument)throw new Error('settings-navigation__group--about is absent from fixed Evidence') const fixedChildren=new Map();for(const node of fixedDocument.nodes){const list=fixedChildren.get(node.parent_index)??[];list.push(node);fixedChildren.set(node.parent_index,list)} const descendants=(root)=>{const output=[],queue=[...(fixedChildren.get(root.node_index)??[])];while(queue.length){const node=queue.shift();output.push(node);queue.push(...(fixedChildren.get(node.node_index)??[]))}return output} const aboutGroup=fixedDocument.nodes.find((node)=>String(node.attributes?.class??'').split(/\s+/).includes('settings-navigation__group--about')),aboutNodes=descendants(aboutGroup) const aboutItem=aboutNodes.find((node)=>String(node.attributes?.class??'').split(/\s+/).includes('settings-navigation__item')),aboutSvg=aboutNodes.find((node)=>node.node_name.toLowerCase()==='svg') if(!aboutItem||!aboutSvg)throw new Error('About navigation item or SVG is absent from fixed Evidence') const fixedSvgRoot=fixedDocument.svg_nodes.find((node)=>node.node_index===aboutSvg.node_index&&node.node_name==='svg'),fixedPath=fixedDocument.svg_nodes.find((node)=>node.parent_index===aboutSvg.node_index&&node.node_name==='path') if(!fixedSvgRoot)throw new Error('About navigation SVG root is absent from fixed Evidence') if(!fixedPath||typeof fixedPath.attributes.d!=='string')throw new Error('About navigation SVG path is absent from fixed Evidence') const pathLength=Buffer.byteLength(fixedPath.attributes.d,'utf16le')/2,pathSha=hash(fixedPath.attributes.d) if(pathLength!==HELP_FEEDBACK_EVIDENCE.pathLength||pathSha!==HELP_FEEDBACK_EVIDENCE.pathSha256)throw new Error('About navigation path fingerprint mismatch: '+pathLength+' '+pathSha) icons.helpFeedback={props:{viewBox:fixedSvgRoot.attributes.viewBox,fill:fixedSvgRoot.attributes.fill,xmlns:'http://www.w3.org/2000/svg'},children:[{tag:'path',props:{transform:fixedPath.attributes.transform,fill:fixedPath.attributes.fill,fillRule:fixedPath.attributes['fill-rule'],d:fixedPath.attributes.d}}]} const generatedReference='// REA '+evidence.evidence_id+'; SettingsNavigation/DC/cXe and source SVG geometry only.\n// About path: REA '+HELP_FEEDBACK_EVIDENCE.id+'; evidence sha256='+fixedEvidenceSha+'; path sha256='+pathSha+'; length='+pathLength+'.\nexport const sourceSettingsTabs = '+JSON.stringify(tabs,null,2)+'\nexport const sourceSettingsGroups = '+JSON.stringify(groups,null,2)+'\nexport const sourceSettingsIcons: Record;children:{tag:string;props:Record}[]}> = '+JSON.stringify(icons,null,2)+'\nconst labels:Record = '+JSON.stringify(labels,null,2)+'\nexport const sourceSettingsLabel=(key:string):string=>labels[key]??key\n' const root=postcss.parse(fs.readFileSync('.tmp/rea-css-recovered/contexts-BkjwYSZT.css','utf8')),css=[] root.walkRules((rule)=>{if(!/\.settings-(?:navigation|search)/.test(rule.selector))return;let copy=rule.clone();copy.selectors=copy.selectors.map((s)=>'.source-settings-navigation '+s);let tree=copy;for(let parent=rule.parent;parent?.type==='atrule';parent=parent.parent){const wrap=parent.clone({nodes:[]});wrap.append(tree);tree=wrap}css.push(tree.toString())}) const variables=new Set();const collect=(text)=>{for(const match of text.matchAll(/var\((--[\w-]+)/g))variables.add(match[1])};collect(css.join(''));const tokenRules=[];postcss.parse(fs.readFileSync('.tmp/rea-css-recovered/tokens-Df-i-zsO.css','utf8')).walkRules((rule)=>tokenRules.push(rule));for(let pass=0;pass<10;pass++)for(const rule of tokenRules)rule.walkDecls((decl)=>{if(variables.has(decl.prop))collect(decl.value)});const light=new Map(),dark=new Map();for(const rule of tokenRules){const target=rule.selector.includes('dark')?dark:rule.selector.includes(':root')?light:null;if(target)rule.walkDecls((decl)=>{if(variables.has(decl.prop))target.set(decl.prop,decl.value)})}const serialize=(map)=>[...map].map(([key,value])=>key+':'+value).join(';');css.unshift('.source-settings-navigation{'+serialize(light)+'}',':root[data-theme=dark] .source-settings-navigation{'+serialize(dark)+'}'); const generatedCss='/* REA ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94; media conditions retained. */\n'+css.join('\n')+'\n',outputs=new Map([['src/renderer/src/components/source-settings-navigation-reference.ts',generatedReference],['src/renderer/src/components/source-settings-navigation-recovered.css',generatedCss]]),drift=[] for(const[file,content]of outputs){if(checkOnly){if(!fs.existsSync(file)||fs.readFileSync(file,'utf8')!==content)drift.push(file)}else fs.writeFileSync(file,content)} if(drift.length){console.error('settings navigation recovery drift: '+drift.join(', '));process.exitCode=1} console.log(JSON.stringify({check:checkOnly,idempotent:drift.length===0,tabs:tabs.length,groups:groups.length,icons:Object.keys(icons),unknownIcons:Object.keys(names).filter((key)=>!icons[key]),rules:css.length,helpFeedbackEvidence:HELP_FEEDBACK_EVIDENCE.id,helpFeedbackPathLength:pathLength,helpFeedbackPathSha256:pathSha}))