// Materialize the focused artifact-preview CSS projection from captured REA evidence. import crypto from 'node:crypto' import fs from 'node:fs/promises' import path from 'node:path' import { fileURLToPath } from 'node:url' import postcss from 'postcss' const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..') const cssDir = path.join(rootDir, '.tmp/rea-css-recovered') const cssManifestPath = path.join(cssDir, 'manifest.json') const runtimePath = path.join(rootDir, '.tmp/rea-runtime-scripts.json') const outputPath = path.join(rootDir, 'src/renderer/src/workbuddy/artifacts/artifact-source.css') const projectionPath = path.join(cssDir, 'artifact-projection.json') const cssEvidenceId = 'ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94' const asarEvidenceId = 'ev_97959691fb7cabf7c41f948b08332196924fec1fa8cb71e34dbafd65d912e637' const asarRootSha256 = '035254bc76310ea46317433e4840602094543ef323d2d5c5aff7c131729de918' const runtimeEvidenceId = 'ev_717fa5e846e64d67ac19c8925d121bff632c0d3aefb3603b10858559cabb2dcb' const cssSpecs = [ { file: 'default-dark-CB7rPan2.css', sha256: '6f1a5b770eceb591ca2e922d075335b170b59ae4e85b2828c176d9a1a5b479fe', runtimeOnly: true }, { file: 'src-BPUOynUo.css', sha256: '02a85e652dd1be508ab2bcf1b67880c3673d4c47e50b2caea24ac205bfe8f879' }, { file: 'src-CyXHA0IC.css', sha256: 'a5e26b537bb50323aefbf25abddad52e288dd3868598be8d255d70886fe77cfe' }, { file: 'media-preview-Dvr0Ekj9.css', sha256: '36758fadd61577145a3b46dd12bcd966c662ea0cfaa4e62dc3e31ba4bf7f5fb8' }, { file: 'image-preview-component-6U_ZZMG_.css', sha256: '17387c5eefa224be5ebde7400f6985aca3da39899db60adecb800de7868ca11c' }, { file: 'media-BjV-0OHV.css', sha256: '2d7d011d57d6c4266f6a7d4465f7d6d0b9894892fe291bd52c3dfe2e9f4b18ab' }, { file: 'tokens-Df-i-zsO.css', sha256: '13f2bf82c96e5167a7d7098e36950c6c22c475b3337dbb8bb29ad2fe79cde696' } ] const runtimeSpecs = [ { file: 'src-Cng7axVa.js', sha256: '5ce8649e3bfac10aec1e63afee726ab45abaafc5aa7cc372892e20f3eba18c8b' }, { file: 'media-preview-DMz4TW4q.js', sha256: 'e8c19693ac7d8d06b45b531de405cc23c071fa19ebd5543b483a43dbdfce8111' }, { file: 'image-preview-component-BgOcW3Xe.js', sha256: '6720317ba018215f96a87d966b08c0d0aa74c26d2298d5fd95f71e8ff2e04e59' }, { file: 'media.module-DC8Y6boy.js', sha256: '2ad55767cb3f3f0302d053022346ecd40921199a16c2b31aff071dc81acbad51' } ] const moduleSelectors = { 'media-preview-DMz4TW4q.js': { object: 'Lo', css: 'media-preview-Dvr0Ekj9.css', keys: { mediaPreview: '.artifact-media-preview', mediaPreviewHeader: '.artifact-media-preview__header', mediaPreviewTitle: '.artifact-media-preview__title', mediaPreviewActions: '.artifact-media-preview__actions', mediaPreviewActionsButton: '.artifact-media-preview__button', mediaPreviewActionsButtonActive: '.artifact-media-preview__button--active', mediaPreviewContent: '.artifact-media-preview__content', mediaPreviewContentFullBleed: '.artifact-media-preview__content--full-bleed', mediaPreviewContentSheet: '.artifact-media-preview__content--sheet' } }, 'media-preview-DMz4TW4q.js#browser': { object: 'Ur', css: 'media-preview-Dvr0Ekj9.css', keys: { browserFrameContainer: '.artifact-html-frame-container', browserFrame: '.artifact-html-frame', browserFrameEmpty: '.artifact-html-frame-empty', browserFrameError: '.artifact-html-frame-error' } }, 'image-preview-component-BgOcW3Xe.js': { object: 'Y', css: 'image-preview-component-6U_ZZMG_.css', keys: { previewOverlay: '.wb-image-preview', previewOverlayClosing: '.wb-image-preview--closing', previewStage: '.wb-image-preview__stage', previewStageClosing: '.wb-image-preview__stage--closing', previewImage: '.wb-image-preview__image', previewCloseActionArea: '.wb-image-preview__close-area', previewCloseButton: '.wb-image-preview__close' } }, 'media.module-DC8Y6boy.js': { object: 'A', css: 'media-BjV-0OHV.css', keys: { bottomCenterActionArea: '.wb-image-preview__toolbar', actionAreaContent: '.wb-image-preview__toolbar-content', actionButtonsArea: '.wb-image-preview__toolbar-buttons', topRightActionArea: '.wb-image-preview__close-area', overlayButton: '.wb-image-preview__control', overlayIcon: '.wb-image-preview__control-icon', scalePercentage: '.wb-image-preview__scale', pageControl: '.wb-image-preview__count', previewContainer: '.wb-image-preview__stage', previewInstance: '.wb-image-preview__image' } } } const digest = bytes => crypto.createHash('sha256').update(bytes).digest('hex') const cssManifest = JSON.parse(await fs.readFile(cssManifestPath, 'utf8')) const runtimeManifest = JSON.parse(await fs.readFile(runtimePath, 'utf8')) if (cssManifest.sourceEvidenceId !== cssEvidenceId) throw new Error(`CSS Evidence changed: expected ${cssEvidenceId}, found ${cssManifest.sourceEvidenceId ?? 'none'}`) if (runtimeManifest.evidence_id !== runtimeEvidenceId) throw new Error(`Runtime Evidence changed: expected ${runtimeEvidenceId}, found ${runtimeManifest.evidence_id ?? 'none'}`) const cssSources = new Map() for (const spec of cssSpecs) { const record = cssManifest.files?.find(item => item.file === spec.file) if (!record || record.sha256 !== spec.sha256) throw new Error(`REA manifest SHA mismatch for ${spec.file}`) if (!spec.runtimeOnly && (record.asarComparison?.status !== 'match' || record.asarComparison.evidenceId !== asarEvidenceId || record.asarComparison.rootSha256 !== asarRootSha256)) { throw new Error(`Static REA comparison is not the locked matching artifact for ${spec.file}`) } const bytes = await fs.readFile(path.join(cssDir, spec.file)) const actual = digest(bytes) if (actual !== spec.sha256) throw new Error(`REA CSS digest mismatch for ${spec.file}: ${actual}`) cssSources.set(spec.file, { ...spec, root: postcss.parse(bytes.toString('utf8'), { from: spec.file }) }) } const runtimeSources = new Map() for (const spec of runtimeSpecs) { const item = runtimeManifest.items?.find(candidate => candidate.file_path?.replace(/\\/g, '/').endsWith(`/assets/${spec.file}`)) const artifact = item?.source?.artifact if (!item || item.frame_id !== cssManifest.target?.target_id || artifact?.media_type !== 'text/javascript' || artifact.sha256 !== spec.sha256 || digest(Buffer.from(artifact.text, 'utf8')) !== spec.sha256) { throw new Error(`REA runtime script digest mismatch or missing source: ${spec.file}`) } runtimeSources.set(spec.file, artifact.text) } const artifactSource = runtimeSources.get('src-Cng7axVa.js') for (const selector of ['artifact-content', 'artifact-body', 'plan-content', 'overview-content', 'markdown-content', 'diff-code-content']) { if (!artifactSource.includes('className:`' + selector + '`')) throw new Error(`Recovered renderer DOM class is missing: ${selector}`) } function moduleMap(script, objectName) { const generated = new Map() for (const match of script.matchAll(/\b([A-Za-z_$][\w$]*)=`([^`]+)`/g)) generated.set(match[1], match[2]) const match = new RegExp(`\\b${objectName}\\s*=\\{([^{}]*)\\}`).exec(script) if (!match) throw new Error(`CSS module object ${objectName} was not found`) const result = new Map() const entryPattern = /(?:([A-Za-z_$][\w$]*)|["']([^"']+)["']):([A-Za-z_$][\w$]*|`[^`]*`)/g for (const entry of match[1].matchAll(entryPattern)) { const key = entry[1] ?? entry[2] const value = entry[3] result.set(key, value.startsWith('`') ? value.slice(1, -1) : generated.get(value)) } return result } const projectedModules = new Map() const classProjection = new Map() for (const [moduleId, spec] of Object.entries(moduleSelectors)) { const [scriptName, role] = moduleId.split('#') const script = runtimeSources.get(scriptName) const objectName = role === 'browser' ? spec.object : spec.object const mapping = moduleMap(script, objectName) const projected = new Map() for (const [key, localSelector] of Object.entries(spec.keys)) { const generatedClass = mapping.get(key) if (!generatedClass) throw new Error(`CSS module key ${scriptName}:${key} is missing`) const selector = `.${generatedClass}` const source = cssSources.get(spec.css) let found = false source.root.walkRules(rule => { if (postcss.list.comma(rule.selector).some(candidate => candidate.includes(selector))) found = true }) if (!found) throw new Error(`CSS module selector ${selector} is missing from ${spec.css}`) projected.set(selector, localSelector) classProjection.set(`${scriptName}:${key}`, { sourceSelector: selector, localSelector, css: spec.css }) } projectedModules.set(moduleId, projected) } const output = postcss.root() const selected = new Map() function appendProjectedRule(rule, projectSelector, source) { const parts = postcss.list.comma(rule.selector) const selectors = parts.flatMap(selector => { const projected = projectSelector(selector) return Array.isArray(projected) ? projected : projected ? [projected] : [] }) if (!selectors.length) return let clone = rule.clone({ selector: [...new Set(selectors)].join(',') }) for (let parent = rule.parent; parent && parent.type !== 'root'; parent = parent.parent) { if (parent.type === 'atrule' && parent.nodes) clone = parent.clone({ nodes: [clone] }) } clone.raws.before = '\n' output.append(clone) selected.set(source, (selected.get(source) ?? 0) + 1) } function rewriteMappedSelector(selector, mappings) { let rewritten = selector let matched = false const moduleTokens = selector.match(/\._[A-Za-z][\w-]*_[A-Za-z0-9]+_\d+/g) ?? [] if (moduleTokens.some(token => !mappings.has(token))) return undefined for (const [sourceSelector, localSelector] of mappings) { if (!rewritten.includes(sourceSelector)) continue rewritten = rewritten.replaceAll(sourceSelector, localSelector) matched = true } if (!matched) return undefined const selectors = [rewritten] if (/(^|\s)(?::root\.(?:dark|cb-dark)|:root\s+\.(?:dark|cb-dark))\s/.test(selector)) { const hostDark = rewritten.replace(/:root\.(?:dark|cb-dark)|:root\s+\.(?:dark|cb-dark)/, ':root[data-theme=dark]') if (hostDark !== rewritten) selectors.push(hostDark) } return selectors } const artifactFamilies = /\.(?:artifact-content|artifact-body|plan-content|overview-content|custom-artifact-content|markdown-content|markdown-viewer|code-content|diff-code-content|diff-loading|diff-error)(?=$|[\s.#:[>+~])/ const directImages = new Set(['.cr-image-block', '.cr-image-block-fallback']) for (const file of ['src-BPUOynUo.css', 'src-CyXHA0IC.css']) { const source = cssSources.get(file) source.root.walkRules(rule => { const project = selector => { if (file === 'src-BPUOynUo.css' && artifactFamilies.test(selector)) return selector.replace(/:global\(([^)]+)\)/g, '$1').replace(/\.markdown-content(?=$|[^\w-])/g, '.artifact-markdown-content') if (file === 'src-CyXHA0IC.css' && directImages.has(selector)) return selector return undefined } appendProjectedRule(rule, project, file) }) } for (const [moduleId, mappings] of projectedModules) { const cssFile = moduleSelectors[moduleId].css const source = cssSources.get(cssFile) source.root.walkRules(rule => { appendProjectedRule(rule, selector => rewriteMappedSelector(selector, mappings), `${cssFile}:${moduleId}`) }) } const workbuddyTokens = new Map([['light', new Map()], ['dark', new Map()]]) // Runtime CSS Evidence locks this theme variable; this file has no separate ASAR comparison. cssSources.get('default-dark-CB7rPan2.css').root.walkRules(rule => { const theme = rule.selector.includes('IDE\\ Night') ? 'dark' : rule.selector.includes('IDE\\ Light') ? 'light' : undefined const background = rule.nodes.find(node => node.type === 'decl' && node.prop === '--vscode-diffCodeContent-background') if (!theme || !background) return output.append(postcss.rule({ selector: theme === 'dark' ? ':root[data-theme=dark] .artifact-content' : ':root .artifact-content', nodes: [background.clone()] })) selected.set(`default-dark-CB7rPan2.css:${theme}`, 1) }) cssSources.get('tokens-Df-i-zsO.css').root.walkRules(rule => { const selectors = postcss.list.comma(rule.selector) const dark = selectors.some(selector => /\[data-theme=dark\]|\.dark|cb-dark|vscode-dark|IDE\\ Night/.test(selector)) const light = selectors.some(selector => selector.includes(':root') || /IDE\\ Light/.test(selector)) const variant = dark ? 'dark' : light ? 'light' : undefined if (!variant) return for (const declaration of rule.nodes.filter(node => node.type === 'decl' && node.prop.startsWith('--wb-'))) { workbuddyTokens.get(variant).set(declaration.prop, declaration) } }) const requiredTokens = new Set() output.walkDecls(declaration => { for (const match of declaration.value.matchAll(/var\((--wb-[\w-]+)/g)) requiredTokens.add(match[1]) }) let discoveredToken = true while (discoveredToken) { discoveredToken = false for (const variant of ['light', 'dark']) { const tokens = workbuddyTokens.get(variant) for (const token of [...requiredTokens]) { const declaration = tokens.get(token) ?? workbuddyTokens.get('light').get(token) if (!declaration) continue for (const match of declaration.value.matchAll(/var\((--wb-[\w-]+)/g)) { if (!requiredTokens.has(match[1])) { requiredTokens.add(match[1]) discoveredToken = true } } } } } for (const [variant, selector] of [ ['light', ':root .artifact-content, :root .artifact-media-preview, :root .wb-image-preview'], ['dark', ':root[data-theme=dark] .artifact-content, :root[data-theme=dark] .artifact-media-preview, :root[data-theme=dark] .wb-image-preview'] ]) { const declarations = [...requiredTokens] .map(name => workbuddyTokens.get(variant).get(name)) .filter(Boolean) .map(declaration => declaration.clone()) if (!declarations.length) continue output.append(postcss.rule({ selector, nodes: declarations })) selected.set(`tokens-Df-i-zsO.css:${variant}`, declarations.length) } output.append(postcss.rule({ selector: '.artifact-content, .artifact-media-preview, .wb-image-preview', nodes: [ ['--cb-bg-secondary', 'var(--yc-bg-secondary)'], ['--cb-hover-bg', 'var(--yc-bg-hover)'], ['--cb-panel-bg-primary', 'var(--yc-bg-primary)'], ['--cr-md-bg-code-block', 'var(--yc-bg-secondary)'], ['--cr-md-text-tertiary', 'var(--yc-text-tertiary)'], ['--vscode-font-family', 'var(--yc-font-sans)'] ].map(([prop, value]) => postcss.decl({ prop, value })) })) const usedKeyframes = new Set() output.walkDecls(declaration => { if (!/^animation(?:-name)?$/.test(declaration.prop)) return const sourceNames = declaration.value.match(/_[A-Za-z][\w-]*_[A-Za-z0-9]+_\d+/g) ?? [] sourceNames.forEach(name => usedKeyframes.add(name)) }) for (const { file } of cssSpecs) { const source = cssSources.get(file) if (!source) continue source.root.walkAtRules(/^keyframes$/, rule => { if (!usedKeyframes.has(rule.params)) return const clone = rule.clone() clone.raws.before = '\n' output.append(clone) selected.set(`${file}:keyframes`, (selected.get(`${file}:keyframes`) ?? 0) + 1) }) } if ((selected.get('src-BPUOynUo.css') ?? 0) < 20) throw new Error('Artifact CSS projection did not recover the expected global preview family') if ((selected.get('src-CyXHA0IC.css') ?? 0) < 2) throw new Error('Markdown image styles were not recovered') const cssHeader = `/* Generated by scripts/rea/recover-artifact-styles.mjs from captured REA Evidence.\n * CSS Evidence ${cssEvidenceId}; runtime Evidence ${runtimeEvidenceId}.\n * Original selected declarations and at-rule/theme conditions are preserved.\n * CSS module classes are projected from locked runtime mappings; source selectors are listed in .tmp/rea-css-recovered/artifact-projection.json.\n */\n` const cssText = `${cssHeader}${output.toString()}\n` postcss.parse(cssText, { from: path.relative(rootDir, outputPath) }) await fs.writeFile(outputPath, cssText, 'utf8') const projection = { cssEvidenceId, asarEvidenceId, asarRootSha256, runtimeEvidenceId, target: cssManifest.target, selected: [...selected].map(([source, rules]) => ({ source, rules })), hostAdapters: { darkTheme: 'Retained source theme selectors and added :root[data-theme=dark] projections for the WorkBuddy color theme.', moduleClasses: 'Locked CSS module selectors are projected to YCode component classes; unneeded dynamic module selectors are omitted.', selector: ':global(.monaco-editor) is projected to .monaco-editor for a native browser stylesheet.', markdownClass: 'Original artifact-only .markdown-content becomes .artifact-markdown-content to avoid colliding with YCode chat Markdown.', hostTokens: ['--cb-bg-secondary', '--cb-hover-bg', '--cb-panel-bg-primary', '--cr-md-bg-code-block', '--cr-md-text-tertiary', '--vscode-font-family'] }, css: cssSpecs.map(spec => ({ ...spec, artifactId: cssManifest.files.find(item => item.file === spec.file).asarComparison?.artifactId, observationScope: spec.runtimeOnly ? 'REA runtime CSS only; no independent ASAR comparison' : 'REA runtime CSS and matching ASAR artifact' })), runtime: runtimeSpecs, moduleProjection: Object.fromEntries([...classProjection].map(([key, value]) => [key, value])) } await fs.writeFile(projectionPath, `${JSON.stringify(projection, null, 2)}\n`, 'utf8') console.log(JSON.stringify({ output: path.relative(rootDir, outputPath), cssEvidenceId, runtimeEvidenceId, selected: projection.selected, projectedModuleClasses: classProjection.size }, null, 2))