feat: complete WorkBuddy parity and Windows release workflow

This commit is contained in:
xiaohan.yuan committed 2026-10-18 12:45:21 +08:00
1 parent 143516b3a8
commit d5512df549
434 files changed
+54977 -1854

No files matched your search

+26 -4
View File
@@ -1,9 +1,12 @@
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()
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
@@ -30,9 +33,28 @@ function resolve(base,name,depth=0){
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}
fs.writeFileSync('src/renderer/src/components/source-settings-navigation-reference.ts','// REA '+evidence.evidence_id+'; SettingsNavigation/DC/cXe and source SVG geometry only.\nexport const sourceSettingsTabs = '+JSON.stringify(tabs,null,2)+'\nexport const sourceSettingsGroups = '+JSON.stringify(groups,null,2)+'\nexport const sourceSettingsIcons: Record<string,{props:Record<string,string|number>;children:{tag:string;props:Record<string,string|number>}[]}> = '+JSON.stringify(icons,null,2)+'\nconst labels:Record<string,string> = '+JSON.stringify(labels,null,2)+'\nexport const sourceSettingsLabel=(key:string):string=>labels[key]??key\n')
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<string,{props:Record<string,string|number>;children:{tag:string;props:Record<string,string|number>}[]}> = '+JSON.stringify(icons,null,2)+'\nconst labels:Record<string,string> = '+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)+'}');
fs.writeFileSync('src/renderer/src/components/source-settings-navigation-recovered.css','/* REA ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94; media conditions retained. */\n'+css.join('\n')+'\n')
console.log(JSON.stringify({tabs:tabs.length,groups:groups.length,icons:Object.keys(icons),unknownIcons:Object.keys(names).filter((key)=>!icons[key]),rules:css.length}))
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}))