chore: establish YCode replication baseline and TODO
This commit is contained in:
commit
6dc59f89ba
1118 files changed
+196002
No files matched your search
@@ -0,0 +1,40 @@
|
||||
/** Only fixed, saved REA evidence is read. --check verifies recovered outputs without writing. */
|
||||
import fs from 'node:fs'
|
||||
import crypto from 'node:crypto'
|
||||
import { parse } from '@babel/parser'
|
||||
import postcss from 'postcss'
|
||||
const sha = value => crypto.createHash('sha256').update(value).digest('hex')
|
||||
const archive = JSON.parse(fs.readFileSync('.tmp/rea-runtime-scripts.json','utf8'))
|
||||
const cssManifest = JSON.parse(fs.readFileSync('.tmp/rea-css-recovered/manifest.json','utf8'))
|
||||
const manifest = {version:'5.8.2',rootSha256:'035254bc76310ea46317433e4840602094543ef323d2d5c5aff7c131729de918',evidence:[archive.evidence_id,cssManifest.sourceEvidenceId],sources:[],bodies:[],adaptations:['Local React/JSX singleton; source component/function bodies are preserved except binding substitutions.', 'ExpertRow disabled adds the typed host item.disabled flag.', 'Source cloud search/recommendation/pin hooks are replaced by the explicit local typed host; no original cloud service or fake expert is invoked.', 'CSS declarations remain unchanged; selectors scoped to .yc-expert-picker.']}
|
||||
function index(name,expected){const a=archive.items.find(i=>i.file_path.endsWith(name))?.source.artifact;if(!a||a.sha256!==expected||sha(a.text)!==expected)throw Error('REA source identity mismatch '+name);manifest.sources.push({file:name,sha256:expected});const nodes=new Map(),strings=new Map();function visit(n){if(!n||typeof n!=='object')return;if(n.type==='FunctionDeclaration'&&n.id)nodes.set(n.id.name,n);if(n.type==='AssignmentExpression'&&n.left.type==='Identifier'){nodes.set(n.left.name,n);if(n.right.type==='TemplateLiteral'&&!n.right.expressions.length)strings.set(n.left.name,n.right.quasis[0].value.cooked);if(n.right.type==='StringLiteral')strings.set(n.left.name,n.right.value)}for(const[key,v]of Object.entries(n)){if(key==='loc'||key==='comments')continue;if(Array.isArray(v))v.forEach(visit);else if(v&&typeof v==='object')visit(v)}}const ast=parse(a.text,{sourceType:'module'});visit(ast.program);return{...a,nodes,strings,ast}}
|
||||
const input=index('wb-input-add-B045GnIg.js','7842fa4321d5351bf6c822bbd0253e0d6d6e68cc9cfae3f2470b3d493c23daf4')
|
||||
const context=index('contexts-BMeWSUHk.js','5f35b7489b107e21ef452c8ee88a95265756f6fc444841eef869228bbc44768b')
|
||||
const locale=index('zh-cn-Dfzn8myY.js','05bdf17cc76e1c2547e84a2754d249405366eb5559149b1661837c9687e6efab')
|
||||
function getBody(source,name){const n=source.nodes.get(name);if(!n)throw Error('Missing body '+name);const original=source.text.slice(n.start,n.end);manifest.bodies.push({name,start:n.start,end:n.end,sha256:sha(original)});return (n.type==='AssignmentExpression'?'const ':'')+original.replace(/\(0,[\w$]+\.(jsx|jsxs)\)/g,' JSX.$1')+';'}
|
||||
function classMap(source,name){const n=source.nodes.get(name)?.right;if(n?.type!=='ObjectExpression')throw Error('Missing class map '+name);return Object.fromEntries(n.properties.map(p=>{const key=p.key.name??p.key.value;const value=p.value.type==='Identifier'?source.strings.get(p.value.name):p.value.type==='StringLiteral'?p.value.value:p.value.quasis?.[0]?.value.cooked;if(typeof value!=='string')throw Error('Unresolved class '+key);return[key,value]}))}
|
||||
const classes={expert:classMap(input,'V'),base:classMap(context,'_V')}
|
||||
const labels={};function labelsWalk(n){if(!n||typeof n!=='object')return;if(n.type==='ObjectProperty'&&n.key.type==='StringLiteral'&&n.key.value.startsWith('input.wbAdd.experts.')){const v=n.value.type==='StringLiteral'?n.value.value:n.value.type==='TemplateLiteral'&&!n.value.expressions.length?n.value.quasis[0].value.cooked:undefined;if(v!==undefined)labels[n.key.value]=v}for(const[key,v]of Object.entries(n)){if(key==='loc'||key==='comments')continue;if(Array.isArray(v))v.forEach(labelsWalk);else if(v&&typeof v==='object')labelsWalk(v)}}labelsWalk(locale.ast.program)
|
||||
if(Object.keys(labels).length<10)throw Error('Expert picker locale messages missing')
|
||||
let body=`import * as React from 'react';import * as JSX from 'react/jsx-runtime';\nimport {usePinnedExperts,useExpertsSuggestions,useExpertSearch,AvatarPrimitive,Tooltip,classNames} from './expert-picker-runtime';\nimport {PinIcon,UnpinIcon,SearchIcon,ClearFilledIcon} from './SkillSourceIcons';\nconst no=React,SV=React,U=JSX,CV=JSX,V=${JSON.stringify(classes.expert)},_V=${JSON.stringify(classes.base)},to={default:classNames},j=()=>key=>(${JSON.stringify(labels)})[key]??key,oe=AvatarPrimitive,C=Tooltip,we=PinIcon,Vt=UnpinIcon,Tt=value=>value;\n`
|
||||
body+=getBody(context,'CheckIcon$2')+'\nconst yt=CheckIcon$2,SearchIcon$1=SearchIcon;\n'+getBody(context,'wV')+'\nconst Pt=wV;\n'
|
||||
for(const name of ['readString','getExpertMenuDisplayName','normalizeKeyword','matchesKeyword','SummonMoreIcon','ExpertAvatar','ExpertRow','ExpertsSubmenu'])body+=getBody(input,name)+'\n'
|
||||
const disabledSource='disabled:i===`search`?!e?.onSummonSearchResult:!e?.onSelect'
|
||||
if(!body.includes(disabledSource))throw Error('Source disabled adaptation boundary changed')
|
||||
body=body.replace(disabledSource,'disabled:!!t.disabled||(i===`search`?!e?.onSummonSearchResult:!e?.onSelect)')
|
||||
const selectedShortCircuit='if(r===n.id){t?.();return}'
|
||||
if(body.split(selectedShortCircuit).length!==2)throw Error('Source selected-row adaptation boundary changed')
|
||||
body=body.replace(selectedShortCircuit,'if(r===n.id&&!e?.reselectSelected){t?.();return}')
|
||||
manifest.adaptations.push('Explicit host adaptation: injection.reselectSelected opts out of the legacy selected-ID close-only branch; YCode enables it for current cr ordinary user reactivation while preserving selected styling. This is not literal legacy behavior. Basis: docs/专家使用逻辑-REA核对-20261016.md, src-DbE1_mgM.js ExpertsSubmenu [1574199,1579192), SHA 6c6ce63c93854783270e855572797d75e536e3b8d4548410e31fe9d147e1feb4; current cr onSelect does not short-circuit the selected ID. The typed host retains its existing async success-close/failure-keep-open behavior.')
|
||||
const rowFocus='role:`menuitem`,"aria-disabled":n'
|
||||
if(!body.includes(rowFocus))throw Error('Source row keyboard boundary changed')
|
||||
body=body.replace(rowFocus,'role:`menuitem`,tabIndex:n?-1:0,"data-expert-id":e.id,"aria-disabled":n').replace('role:`menuitem`,onClick:handleSummonMore','role:`menuitem`,tabIndex:0,"data-expert-manage":true,onClick:handleSummonMore')
|
||||
manifest.adaptations.push('Expert row and summon-more receive explicit tabindex/data attributes for direct host keyboard navigation; source parent has no Arrow-key row traversal.')
|
||||
body+='export {ExpertsSubmenu,ExpertRow,getExpertMenuDisplayName,normalizeKeyword,matchesKeyword};\n'
|
||||
let css='/* REA fixed _14qob_ expert menu + _1mrhu_ search/base declarations. */\n'
|
||||
for(const [file,expected] of Object.entries({'contexts-BkjwYSZT.css':'50cbd673f7f35fc7f966bdddc6c9f5677fdfe944cb904888d214619fca259380','wb-input-add-CDNucW11.css':'07566131dac43d1854af1ecb973bf9736a63723a0786ba0d2d5287ed6f45f2f6'})){const text=fs.readFileSync('.tmp/rea-css-recovered/'+file,'utf8'),entry=cssManifest.files.find(f=>f.file===file);if(!entry||sha(text)!==expected||entry.sha256!==expected)throw Error('CSS identity mismatch '+file);manifest.sources.push({file,sha256:entry.sha256});const tree=postcss.parse(text);tree.walkRules(rule=>{if(!/_14qob_|_1mrhu_/.test(rule.selector))return;const clone=rule.clone();clone.selector=clone.selectors.map(s=>'.yc-expert-picker '+s).join(',');if(rule.parent.type==='atrule'){const wrapper=rule.parent.clone({nodes:[]});wrapper.append(clone);css+=wrapper.toString()+'\n'}else css+=clone.toString()+'\n'})}
|
||||
const declarations=`import type {ComponentType} from 'react';import type {ExpertPickerItem} from './SourceExpertPicker';\nexport const ExpertsSubmenu:ComponentType<{injection:{selectedExpertId?:string|null;reselectSelected?:boolean;expertScope:string;recentExperts:readonly ExpertPickerItem[];loadFailed?:boolean;onSelect?:(item:ExpertPickerItem)=>unknown;onSummonSearchResult?:(item:ExpertPickerItem)=>unknown;onSummonMore?():void};onCloseMenu():void}>;\nexport function getExpertMenuDisplayName(item:ExpertPickerItem):string;export function normalizeKeyword(value:string):string;export function matchesKeyword(item:ExpertPickerItem,query:string):boolean;\n`
|
||||
const outputs=[['src/renderer/src/capabilities/expert-picker-source.mjs',body],['src/renderer/src/capabilities/expert-picker-source.d.mts',declarations],['src/renderer/src/capabilities/expert-picker-classes.json',JSON.stringify(classes,null,2)+'\n'],['src/renderer/src/capabilities/expert-picker-source.css',css]]
|
||||
manifest.outputs=outputs.map(([file,text])=>({file,sha256:sha(text),bytes:Buffer.byteLength(text)}));outputs.push(['src/renderer/src/capabilities/expert-picker-manifest.json',JSON.stringify(manifest,null,2)+'\n'])
|
||||
for(const[file,text]of outputs){if(process.argv.includes('--check')){if(fs.readFileSync(file,'utf8')!==text)throw Error('Recovered output drift '+file)}else fs.writeFileSync(file,text)}
|
||||
console.log(JSON.stringify({result:'PASS',sources:manifest.sources.length,bodies:manifest.bodies.length,labels:Object.keys(labels).length,outputs:outputs.length}))
|
||||
Reference in new issue
Block a user