Files
ycode/scripts/rea/recover-workspace-styles.mjs
T

100 lines
7.5 KiB
JavaScript

// Scope selected workspace and modal rules from the fixed REA CSS export. No original app access.
import { readFile, writeFile } from 'node:fs/promises'
import { createHash } from 'node:crypto'
import { resolve } from 'node:path'
import postcss from 'postcss'
const directory = resolve('.tmp/rea-css-recovered')
const manifest = JSON.parse(await readFile(resolve(directory, 'manifest.json'), 'utf8'))
const evidenceId = 'ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94'
if (manifest.sourceEvidenceId !== evidenceId) throw new Error('Workspace CSS evidence changed')
const sheets = []
for (const file of manifest.files) {
const bytes = await readFile(resolve(directory, file.file))
if (createHash('sha256').update(bytes).digest('hex') !== file.sha256) throw new Error(`Fixed REA CSS digest mismatch: ${file.file}`)
sheets.push({ file: file.file, root: postcss.parse(bytes, { from: file.file }) })
}
const output = postcss.root()
let ruleCount = 0
const selected = (file, selector) => file === 'contexts-BkjwYSZT.css'
? /create-workspace-modal|workspace-more|workspace-context-action|workspace-new-task|conversation-section|conversation-group-empty|conversation-show-more|conversation-agent-card|collapsible-section/.test(selector)
: file === 'src-CBaV_A3S.css' ? /_13lf4_/.test(selector)
: file === 'src-Bh3YZ35H.css' ? /^\.wb-modal|^\.wb-input(?:[\s.:_\[-]|$)/.test(selector) : false
function project(node, file) {
if (node.type === 'rule') {
const selectors = node.selectors.filter(selector => selected(file, selector))
if (!selectors.length) return null
ruleCount++
return node.clone({ selector: selectors.map(selector => `.workspace-source ${selector}`).join(',') })
}
if (node.type === 'atrule' && node.nodes && !/keyframes$/.test(node.name)) {
const children = node.nodes.map(child => project(child, file)).filter(Boolean)
return children.length ? node.clone({ nodes: children }) : null
}
return null
}
// Preserve primitive-before-feature ordering and all media conditions.
for (const file of ['src-Bh3YZ35H.css', 'src-CBaV_A3S.css', 'contexts-BkjwYSZT.css']) {
const sheet = sheets.find(sheet => sheet.file === file)
for (const node of sheet.root.nodes) { const projected = project(node, file); if (projected) output.append(projected) }
}
const needed = new Set()
const refs = text => [...text.matchAll(/var\((--[\w-]+)/g)].map(match => match[1])
for (const ref of refs(output.toString())) needed.add(ref)
let changed = true
while (changed) {
changed = false
for (const sheet of sheets) sheet.root.walkDecls(decl => {
if (!needed.has(decl.prop)) return
for (const ref of refs(decl.value)) if (!needed.has(ref)) { needed.add(ref); changed = true }
})
}
const tokens = postcss.root()
for (const sheet of sheets) sheet.root.walkRules(rule => {
if (!rule.selectors.some(selector => selector === ':root' || /data-theme.*dark/.test(selector))) return
const declarations = rule.nodes.filter(node => node.type === 'decl' && needed.has(node.prop))
if (!declarations.length) return
const dark = !rule.selectors.includes(':root') && rule.selectors.some(selector => /dark/.test(selector))
tokens.append(postcss.rule({ selector: dark ? '[data-theme="dark"] .workspace-source' : '.workspace-source', nodes: declarations.map(decl => decl.clone()) }))
})
const adapters = `
/* Host DOM/accessibility adapters; source rule bodies above remain intact. */
.workspace-source{--cb-font-size-offset:0;--cb-sidebar-bg:var(--yc-bg-secondary);--cb-text-primary:var(--yc-text-primary);font-family:var(--vscode-font-family, "PingFang SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif)}
.workspace-source .workspace-ui-button{border:0;padding:0;background:transparent;font:inherit;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;flex-shrink:0}
.workspace-source .conversation-section-label button{border:0;padding:0;background:transparent}
.workspace-source .collapsible-section-title{gap:8px;padding:0}
.workspace-source .collapsible-section-label{font-size:14px;line-height:22px;font-weight:400}
.workspace-source .workspace-group-actions{display:flex;align-items:center;gap:4px}
.workspace-source .workspace-group-header:focus-within ._customButton_13lf4_31{opacity:1;visibility:visible}
.workspace-source .workspace-group-selected .collapsible-section-label{font-weight:500}
.workspace-source .conversation-section-content{min-width:0}
.workspace-source .workspace-source-task{padding:5px 12px;min-height:32px;gap:8px;display:flex;align-items:center}
.workspace-source .workspace-source-task .sidebar__item-title{font-size:14px;line-height:22px}
.workspace-source .workspace-source-task .sidebar__item-time{font-size:11px;line-height:20px}
.workspace-source .workspace-source-task[data-selected=true] .sidebar__item-title{font-weight:600}
.workspace-source .conversation-section-label:focus-within .conversation-section-add-btn,.workspace-source .conversation-section-label:focus-within .conversation-section-compact-btn{visibility:visible;opacity:1}
.workspace-source .workspace-attach-row{padding:8px 12px;background:transparent;border:0;color:var(--wb-color-text-tertiary);font:inherit;font-size:12px;text-align:left;cursor:pointer}
.workspace-source .workspace-attach-row:hover{color:var(--wb-color-text-primary)}
.workspace-source .workspace-menu-remove{color:var(--wb-color-text-primary)}
.workspace-source .workspace-dialog-error{color:var(--yc-status-error);font-size:13px;line-height:20px;margin-top:12px}
.workspace-source .workspace-dialog-long-name{overflow-wrap:anywhere;white-space:normal}
.workspace-source .wb-modal__overlay{position:fixed;inset:0;z-index:1100;box-sizing:border-box}
.workspace-source .wb-modal{box-sizing:border-box;width:480px;max-width:calc(100vw - 32px)}
.workspace-source .wb-modal__close{width:24px;height:24px;border:0;background:transparent;color:var(--wb-button-ghost-fg,#000000e6);display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0}
.workspace-source .wb-modal__close svg{width:16px;height:16px}
.workspace-source .wb-input-wrapper{box-sizing:border-box;display:flex;align-items:center;width:100%}
.workspace-source .create-workspace-modal__input>input{width:100%;min-width:0;background:transparent;border:0;outline:0;padding:0;font:inherit;font-size:14px;line-height:22px;color:var(--wb-color-text-primary)}
.workspace-source .workspace-dialog-clear{border:0;background:transparent;padding:0;display:flex;width:16px;height:16px;cursor:pointer;color:var(--wb-color-text-tertiary)}
.workspace-source .workspace-dialog-clear svg{width:14px;height:14px}
.workspace-source .create-workspace-modal__confirm,.workspace-source .create-workspace-modal__cancel{cursor:pointer}
.workspace-source .create-workspace-modal__cancel:disabled{opacity:.5;cursor:default}
@media(prefers-reduced-motion:reduce){.workspace-source .wb-modal,.workspace-source .wb-modal__overlay{transition:none}}
`
const text = `/* Generated from fixed REA Evidence ${evidenceId} by scripts/rea/recover-workspace-styles.mjs.
* Runtime JSX: ev_717fa5e846e64d67ac19c8925d121bff632c0d3aefb3603b10858559cabb2dcb, WorkBuddy 5.8.2.
* Scope: local workspace sections, source CollapsibleSection primitives, create modal, context menu.
* ${ruleCount} selected rules; source nesting/media conditions retained. */\n${tokens.toString()}\n${output.toString()}\n${adapters}`
postcss.parse(text)
await writeFile(resolve('src/renderer/src/components/workspace-source.css'), text)
console.log(JSON.stringify({ evidenceId, ruleCount, tokens: needed.size, bytes: Buffer.byteLength(text) }))