75 lines
6.5 KiB
JavaScript
75 lines
6.5 KiB
JavaScript
// Deterministic recovery of retained REA CSS/SVG bodies, without original app access or initialization.
|
|
import fs from 'node:fs/promises'
|
|
import path from 'node:path'
|
|
import { createHash } from 'node:crypto'
|
|
import assert from 'node:assert/strict'
|
|
import postcss from 'postcss'
|
|
import { parse } from '@babel/parser'
|
|
const sha = bytes => createHash('sha256').update(bytes).digest('hex')
|
|
const runtime = JSON.parse(await fs.readFile('.tmp/rea-runtime-scripts.json', 'utf8'))
|
|
const selected = runtime.items.find(item => item.file_path.endsWith('skills-panel-CvMlV3m8.js')).source.artifact
|
|
assert.equal(sha(selected.text), selected.sha256)
|
|
const destination = 'src/renderer/src/capabilities'
|
|
await fs.mkdir(destination, { recursive: true })
|
|
const manifest = JSON.parse(await fs.readFile('.tmp/rea-css-recovered/manifest.json', 'utf8'))
|
|
const source = manifest.files.find(file => file.file === 'skills-panel-CsnIUki4.css')
|
|
const cssText = await fs.readFile(path.join('.tmp/rea-css-recovered', source.file), 'utf8')
|
|
assert.equal(sha(cssText), source.sha256)
|
|
const css = postcss.parse(cssText)
|
|
css.walkRules(rule => { if (rule.parent?.type === 'atrule' && /keyframes$/.test(rule.parent.name)) return; rule.selectors = rule.selectors.map(selector => `:where(.ycode-connectors) :is(${selector})`) })
|
|
const primitives = postcss.root(), primitiveFiles = []
|
|
for (const file of manifest.files) {
|
|
const text = await fs.readFile(path.join('.tmp/rea-css-recovered', file.file), 'utf8')
|
|
assert.equal(sha(text), file.sha256)
|
|
let included = false
|
|
postcss.parse(text).walkRules(rule => {
|
|
const selectors = rule.selectors.filter(selector => /^\.(?:wb-(?:button|input)|cb-search-input)(?:[\s.:#[_-]|$)/.test(selector))
|
|
if (!selectors.length) return
|
|
const copy = rule.clone(); copy.selectors = selectors.map(selector => `:where(.ycode-connectors) :is(${selector})`)
|
|
primitives.append(copy); included = true
|
|
})
|
|
if (included) primitiveFiles.push({ file: file.file, sha256: file.sha256 })
|
|
}
|
|
const cssHeader = `/* REA ${manifest.sourceEvidenceId}; ${source.file} SHA256 ${source.sha256}. Rule bodies retained; selector scope adapter only. */\n`
|
|
await fs.writeFile(path.join(destination, 'connectors-source.css'), cssHeader + css.toString() + '\n/* Retained shared primitive bodies. */\n' + primitives.toString() + '\n')
|
|
const names = new Set(['ChevronIcon', 'CopyIcon', 'ConnectorAddIcon', 'ConnectorLoadingIcon', 'ConnectorCancelIcon', 'RefreshIcon', 'CloseIcon', 'ConnectorDetailTryIcon', 'ConnectorDetailUnbindIcon', 'ConnectorEmptyIcon'])
|
|
const icons = [], ast = parse(selected.text, { sourceType: 'module' })
|
|
let buddy
|
|
function walk(node) {
|
|
if (!node || typeof node !== 'object') return
|
|
if (node.type === 'AssignmentExpression' && names.has(node.left?.name)) icons.push({ name: node.left.name, start: node.right.start, end: node.right.end,
|
|
body: selected.text.slice(node.right.start, node.right.end).replace(/\(0,[\w$]+\.(jsx|jsxs)\)/g, '$1') })
|
|
if (node.type === 'ObjectProperty' && node.key?.name === 'src' && node.start > 78950 && node.value?.type === 'TemplateLiteral') {
|
|
const value = node.value.quasis[0]?.value.cooked
|
|
if (value?.startsWith('data:image/svg+xml,')) buddy ??= value
|
|
}
|
|
for (const [key, child] of Object.entries(node)) if (key !== 'loc') { if (Array.isArray(child)) child.forEach(walk); else if (child && typeof child === 'object') walk(child) }
|
|
}
|
|
walk(ast)
|
|
assert.equal(icons.length, names.size)
|
|
const iconHeader = `// @ts-nocheck\n/* REA ${runtime.evidence_id}; skills-panel-CvMlV3m8.js SHA256 ${selected.sha256}. Literal isolated SVG function bodies; original global initialization is not executed. */\nimport {jsx,jsxs} from 'react/jsx-runtime'\n`
|
|
function retainedSvg(layout, rootIndex) {
|
|
const nodes = layout.layout.documents[0].svg_nodes
|
|
function render(node) {
|
|
const children = nodes.filter(child => child.parent_index === node.node_index)
|
|
const names = { class: 'className', 'fill-rule': 'fillRule', 'clip-rule': 'clipRule', 'stroke-width': 'strokeWidth', 'stroke-linecap': 'strokeLinecap', 'stroke-linejoin': 'strokeLinejoin' }
|
|
const props = Object.fromEntries(Object.entries(node.attributes).map(([key, value]) => [names[key] || key, value]))
|
|
return `${children.length > 1 ? 'jsxs' : 'jsx'}(${JSON.stringify(node.node_name)},${JSON.stringify(props).slice(0, -1)}${children.length ? ',children:' + (children.length === 1 ? render(children[0]) : '[' + children.map(render).join(',') + ']') : ''}})`
|
|
}
|
|
return render(nodes.find(node => node.node_index === rootIndex))
|
|
}
|
|
const storeLayout = JSON.parse(await fs.readFile('.tmp/rea-market-connectors-static-20261016/layout.json', 'utf8'))
|
|
const modalLayout = JSON.parse(await fs.readFile('.tmp/rea-custom-connectors-static-20261016/layout.json', 'utf8'))
|
|
const modalSvg = modalLayout.layout.documents[0].svg_nodes
|
|
const addSvg = modalSvg.find(node => node.node_name === 'svg' && node.node_index > 1609 && node.node_index < 1613)
|
|
const hubSvg = modalSvg.find(node => node.node_name === 'svg' && node.node_index > 1637 && node.node_index < 1641)
|
|
const modalNodes = modalLayout.layout.documents[0].nodes
|
|
const closeButton = modalNodes.find(node => node.attributes?.class === 'mcp-modal-close')
|
|
const closeSvg = modalSvg.find(node => node.node_name === 'svg' && node.parent_index === closeButton.node_index)
|
|
const domIcons = `\n/* REA ${storeLayout.evidenceId}, node821 */\nexport const ConnectorEditIcon=()=>${retainedSvg(storeLayout, 821)}\n/* REA ${modalLayout.evidenceId}, literal modal SVGs */\nexport const McpAddIcon=()=>${retainedSvg(modalLayout, addSvg.node_index)}\nexport const McpHubIcon=()=>${retainedSvg(modalLayout, hubSvg.node_index)}\nexport const McpCloseIcon=()=>${retainedSvg(modalLayout, closeSvg.node_index)}\nexport const McpSearchIcon=()=>${retainedSvg(modalLayout, 1631)}\nexport const McpClearIcon=()=>${retainedSvg(modalLayout, 1635)}\n`
|
|
await fs.writeFile(path.join(destination, 'ConnectorSourceIcons.tsx'), iconHeader + icons.map(icon => `// UTF-16 [${icon.start},${icon.end})\nexport const ${icon.name}=${icon.body}`).join('\n') + '\nexport const SOURCE_BUDDY_ICON=' + JSON.stringify(buddy) + '\n' + domIcons)
|
|
await fs.mkdir('.tmp/mcp-source-ui', { recursive: true })
|
|
await fs.writeFile('.tmp/mcp-source-ui/recovery.json', JSON.stringify({ evidenceId: runtime.evidence_id, cssEvidenceId: manifest.sourceEvidenceId,
|
|
scriptSha256: selected.sha256, cssSha256: source.sha256, primitiveFiles, icons: icons.map(({ body, ...icon }) => icon) }, null, 2))
|
|
console.log('MCP_SOURCE_UI_PASS ' + icons.length + ' icons; ' + primitiveFiles.length + ' primitive sheets')
|