77 lines
5.4 KiB
JavaScript
77 lines
5.4 KiB
JavaScript
// Consume only previously captured REA artifacts; never access the installed original.
|
|
import { readFile, writeFile, mkdir } from 'node:fs/promises'
|
|
import { createHash } from 'node:crypto'
|
|
import { parse } from '@babel/parser'
|
|
import postcss from 'postcss'
|
|
const output = 'src/renderer/src/workbuddy/workbench'
|
|
await mkdir(output, { recursive: true })
|
|
const evidence = JSON.parse(await readFile('.tmp/rea-runtime-scripts.json', 'utf8'))
|
|
if (evidence.evidence_id !== 'ev_717fa5e846e64d67ac19c8925d121bff632c0d3aefb3603b10858559cabb2dcb') throw Error('REA script Evidence changed')
|
|
const specs = [
|
|
['src-DwHViF_l.js', '1bae5ee8385485c61494aae6f09828a0cb1bb867b761a0761330ceb30aaff60d', ['CloseIcon']],
|
|
['src-Cng7axVa.js', '5ce8649e3bfac10aec1e63afee726ab45abaafc5aa7cc372892e20f3eba18c8b', ['FolderEntryIcon', 'ChangesEntryIcon', 'TerminalEntryIcon', 'BrowserEntryIcon']]
|
|
]
|
|
const icons = []
|
|
for (const [file, sha, names] of specs) {
|
|
const source = evidence.items.find(item => item.file_path.endsWith(file))?.source?.artifact?.text
|
|
if (!source || createHash('sha256').update(source).digest('hex') !== sha) throw Error(`${file} hash changed`)
|
|
const found = new Map()
|
|
const visit = node => {
|
|
if (!node || typeof node !== 'object') return
|
|
if (node.type === 'FunctionDeclaration' && names.includes(node.id?.name)) found.set(node.id.name,
|
|
source.slice(node.start, node.end).replace(/\(0,[A-Za-z0-9_$]+\.jsx\)/g, ' jsx'))
|
|
for (const value of Object.values(node)) if (Array.isArray(value)) value.forEach(visit); else if (value && typeof value === 'object') visit(value)
|
|
}
|
|
visit(parse(source, { sourceType: 'module' }))
|
|
for (const name of names) {
|
|
const fn = found.get(name)
|
|
if (!fn || !fn.includes('jsx(`svg`')) throw Error(`Missing pure SVG function ${name}`)
|
|
icons.push(`export ${fn}`)
|
|
}
|
|
}
|
|
await writeFile(`${output}/SourceWorkbenchIcons.tsx`, `// @ts-nocheck\n// Exact pure SVG functions recovered from REA ${evidence.evidence_id}.\nimport { jsx } from 'react/jsx-runtime'\n${icons.join('\n')}\n`)
|
|
// Artifact list uses the shared solid file icon family, rather than emoji or extension badges.
|
|
const fileIconNames = ['ArtifactDocSolidIcon', 'ArtifactSheetSolidIcon', 'ArtifactSlideSolidIcon', 'ArtifactPdfSolidIcon',
|
|
'ArtifactMarkdownSolidIcon', 'ArtifactHtmlSolidIcon', 'ArtifactCodeSolidIcon', 'ArtifactTextSolidIcon',
|
|
'ArtifactArchiveSolidIcon', 'ArtifactDrawioSolidIcon', 'ArtifactImageSolidIcon', 'ArtifactVideoSolidIcon',
|
|
'ArtifactAudioSolidIcon', 'ArtifactFolderSolidIcon', 'ArtifactUnknownSolidIcon']
|
|
const iconSource = evidence.items.find(item => item.file_path.endsWith('src-DPDj7_qD.js'))?.source?.artifact?.text
|
|
if (!iconSource || createHash('sha256').update(iconSource).digest('hex') !== '747e41d215375e1214c366213aea7e077d85421923029c61cbb6e9562be58de0') throw Error('File icon source changed')
|
|
const fileIcons = new Map()
|
|
const findFileIcons = node => {
|
|
if (!node || typeof node !== 'object') return
|
|
if (node.type === 'AssignmentExpression' && node.left.type === 'Identifier' && fileIconNames.includes(node.left.name)
|
|
&& node.right.type === 'ArrowFunctionExpression') {
|
|
const text = iconSource.slice(node.right.start, node.right.end).replace(/\(0,[A-Za-z0-9_$]+\.(jsx|jsxs)\)/g, ' $1')
|
|
if (!/ (jsx|jsxs)\(`svg`/.test(text)) throw Error(`File icon is not a pure SVG ${node.left.name}`)
|
|
fileIcons.set(node.left.name, `export const ${node.left.name}: (props?: Record<string, unknown>) => React.JSX.Element = ${text}`)
|
|
}
|
|
for (const value of Object.values(node)) if (Array.isArray(value)) value.forEach(findFileIcons); else if (value && typeof value === 'object') findFileIcons(value)
|
|
}
|
|
findFileIcons(parse(iconSource, { sourceType: 'module' }))
|
|
if (fileIcons.size !== fileIconNames.length) throw Error('File icon family incomplete')
|
|
await writeFile(`${output}/SourceFileIcons.tsx`, `// @ts-nocheck\n// Pure solid SVGs from pinned REA ${evidence.evidence_id}, src-DPDj7_qD.js.\nimport { jsx, jsxs } from 'react/jsx-runtime'\n${[...fileIcons.values()].join('\n')}\n`)
|
|
const cssSpecs = [
|
|
['src-BPUOynUo.css', '02a85e652dd1be508ab2bcf1b67880c3673d4c47e50b2caea24ac205bfe8f879'],
|
|
['src-DYMqy4oz.css', '420035cb969d5577ca19bf6d7b4e44f670580b51ec8987b6138728eb0416e060']
|
|
]
|
|
const selected = postcss.root()
|
|
let count = 0
|
|
function filter(node) {
|
|
if (node.type === 'rule') {
|
|
if (!/sidebar-next|artifact-tab|detail-new-tab|detail-layout|detail-main|detail-panel|detail-sidebar|fc-sidebar|fc-file|file-tree|cb-overview-artifact/.test(node.selector)) return null
|
|
count++; return node.clone()
|
|
}
|
|
if (node.type !== 'atrule') return null
|
|
const result = node.clone({ nodes: [] })
|
|
for (const child of node.nodes ?? []) { const match = filter(child); if (match) result.append(match) }
|
|
return result.nodes.length ? result : null
|
|
}
|
|
for (const [file, sha] of cssSpecs) {
|
|
const text = await readFile(`.tmp/rea-css-recovered/${file}`, 'utf8')
|
|
if (createHash('sha256').update(text).digest('hex') !== sha) throw Error(`${file} hash changed`)
|
|
for (const node of postcss.parse(text).nodes) { const match = filter(node); if (match) selected.append(match) }
|
|
}
|
|
await writeFile(`${output}/workbench-source.css`, `/* Exact rule bodies and parent conditions. REA ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94. */\n${selected.toString()}\n`)
|
|
console.log(`Recovered ${icons.length} entry SVGs, ${fileIcons.size} solid file SVGs and ${count} source rules with at-rule ancestry preserved`)
|