128 lines
5.8 KiB
JavaScript
128 lines
5.8 KiB
JavaScript
// Recover only the TaskToolRenderer rules from already-exported REA Evidence.
|
|
import { createHash } from 'node:crypto'
|
|
import { readFile, writeFile } from 'node:fs/promises'
|
|
import { dirname, relative, resolve } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import postcss from 'postcss'
|
|
|
|
const rootDir = resolve(dirname(fileURLToPath(import.meta.url)), '../..')
|
|
const runtimeEvidencePath = resolve(rootDir, '.tmp/rea-runtime-scripts.json')
|
|
const cssDirectory = resolve(rootDir, '.tmp/rea-css-recovered')
|
|
const outputPath = resolve(rootDir, 'src/renderer/src/workbuddy/conversation/task-source.css')
|
|
|
|
const runtimeEvidenceId = 'ev_717fa5e846e64d67ac19c8925d121bff632c0d3aefb3603b10858559cabb2dcb'
|
|
const runtimeScriptPath = 'renderer/assets/src-0FGg1seX.js'
|
|
const runtimeScriptSha256 = 'f2c6dbae949041b4e2bb456e8a23ae2837f2d7893ac24bf471485f77c7b15178'
|
|
const cssEvidenceId = 'ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94'
|
|
const cssFile = 'src-CBaV_A3S.css'
|
|
const cssSha256 = '2090ed21045c8f8b0fc388d76424c8751bf18fcd065cbbd5d0ef485163a2e36c'
|
|
const sourceSelector = '.cb-task-tool-renderer'
|
|
|
|
function digest(bytes) {
|
|
return createHash('sha256').update(bytes).digest('hex')
|
|
}
|
|
|
|
function verifyArtifact(label, bytes, expectedSha256) {
|
|
const actual = digest(bytes)
|
|
if (actual !== expectedSha256) throw new Error(`${label} SHA-256 mismatch: expected ${expectedSha256}, got ${actual}`)
|
|
}
|
|
|
|
const runtimeEvidence = JSON.parse(await readFile(runtimeEvidencePath, 'utf8'))
|
|
if (runtimeEvidence.evidence_id !== runtimeEvidenceId) {
|
|
throw new Error(`Runtime Evidence changed: expected ${runtimeEvidenceId}, got ${runtimeEvidence.evidence_id ?? 'none'}`)
|
|
}
|
|
|
|
const runtimeMatches = runtimeEvidence.items.filter((item) => item.file_path?.replace(/\\/g, '/').endsWith(runtimeScriptPath))
|
|
if (runtimeMatches.length !== 1) throw new Error(`Expected one REA runtime script at ${runtimeScriptPath}, found ${runtimeMatches.length}`)
|
|
const runtimeSource = runtimeMatches[0].source
|
|
const runtimeArtifact = runtimeSource?.artifact
|
|
if (!runtimeSource?.included || runtimeArtifact?.media_type !== 'text/javascript') throw new Error('REA TaskToolRenderer source is not included as JavaScript')
|
|
const runtimeBytes = Buffer.from(runtimeArtifact.text, 'utf8')
|
|
verifyArtifact(runtimeScriptPath, runtimeBytes, runtimeScriptSha256)
|
|
if (runtimeArtifact.sha256 !== runtimeScriptSha256 || runtimeArtifact.bytes !== runtimeBytes.length) {
|
|
throw new Error(`REA runtime artifact metadata mismatch for ${runtimeScriptPath}`)
|
|
}
|
|
for (const marker of ['TaskToolRenderer=', 'cb-task-result-item-title', 'cb-task-result-item-border', 'cb-task-tool-info-text', 'cb-task-hidden']) {
|
|
if (!runtimeArtifact.text.includes(marker)) throw new Error(`REA TaskToolRenderer source is missing ${marker}`)
|
|
}
|
|
|
|
const cssManifest = JSON.parse(await readFile(resolve(cssDirectory, 'manifest.json'), 'utf8'))
|
|
if (cssManifest.sourceEvidenceId !== cssEvidenceId) {
|
|
throw new Error(`CSS Evidence changed: expected ${cssEvidenceId}, got ${cssManifest.sourceEvidenceId ?? 'none'}`)
|
|
}
|
|
const cssMetadata = cssManifest.files?.find((file) => file.file === cssFile)
|
|
if (!cssMetadata || cssMetadata.sha256 !== cssSha256) throw new Error(`REA CSS manifest has no expected digest for ${cssFile}`)
|
|
const cssBytes = await readFile(resolve(cssDirectory, cssFile))
|
|
verifyArtifact(cssFile, cssBytes, cssSha256)
|
|
if (cssMetadata.bytes !== cssBytes.length) throw new Error(`REA CSS byte count mismatch for ${cssFile}`)
|
|
|
|
const root = postcss.parse(cssBytes, { from: cssFile })
|
|
const rootSelector = `[data-source-evidence-id="${runtimeEvidenceId}"][data-css-evidence-id="${cssEvidenceId}"].cb-task-tool-renderer`
|
|
const outputRoot = postcss.root()
|
|
let ruleCount = 0
|
|
|
|
function project(node) {
|
|
if (node.type === 'rule') {
|
|
const selectors = (node.selectors ?? []).filter((selector) => selector.includes(sourceSelector))
|
|
if (selectors.length === 0) return null
|
|
const projected = selectors.map((selector) => {
|
|
if (!selector.startsWith(sourceSelector)) throw new Error(`Unexpected TaskToolRenderer selector root: ${selector}`)
|
|
return selector.replace(sourceSelector, rootSelector)
|
|
})
|
|
ruleCount += 1
|
|
return node.clone({ selector: projected.join(',') })
|
|
}
|
|
if (node.type === 'atrule' && /keyframes$/.test(node.name)) {
|
|
return node.params === 'cb-task-pulse' ? node.clone() : null
|
|
}
|
|
if (node.type === 'atrule' && node.nodes) {
|
|
const children = node.nodes.map(project).filter(Boolean)
|
|
return children.length ? node.clone({ nodes: children }) : null
|
|
}
|
|
return null
|
|
}
|
|
|
|
for (const node of root.nodes) {
|
|
const projected = project(node)
|
|
if (projected) outputRoot.append(projected)
|
|
}
|
|
if (ruleCount !== 40) throw new Error(`Expected 40 TaskToolRenderer CSS rules, found ${ruleCount}`)
|
|
|
|
const header = `/* Generated by scripts/rea/recover-task-styles.mjs from fixed REA Evidence.
|
|
* Runtime JSX: ${runtimeEvidenceId}; ${runtimeScriptPath} sha256=${runtimeScriptSha256}.
|
|
* Source CSS: ${cssEvidenceId}; ${cssFile} sha256=${cssSha256}.
|
|
* Original selected rule bodies are preserved and scoped to the matching DOM evidence markers.
|
|
* Native button selectors below adapt the original clickable div and SVG controls for keyboard access.
|
|
*/\n`
|
|
const buttonAdapters = `
|
|
${rootSelector} .cb-task-result-item-title {
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
border: 0;
|
|
color: inherit;
|
|
font-family: inherit;
|
|
font-size: inherit;
|
|
text-align: left;
|
|
}
|
|
${rootSelector} .cb-task-status-icon {
|
|
border: 0;
|
|
padding: 0;
|
|
background: transparent;
|
|
font: inherit;
|
|
}
|
|
`
|
|
const output = `${header}${outputRoot.toString()}${buttonAdapters}`
|
|
postcss.parse(output, { from: relative(rootDir, outputPath) })
|
|
await writeFile(outputPath, output, 'utf8')
|
|
|
|
console.log(JSON.stringify({
|
|
runtimeEvidenceId,
|
|
runtimeScriptPath,
|
|
runtimeScriptSha256,
|
|
cssEvidenceId,
|
|
cssFile,
|
|
cssSha256,
|
|
selectedRules: ruleCount,
|
|
destination: relative(rootDir, outputPath)
|
|
}, null, 2))
|