mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
chore(hygiene): убрать мёртвые скрипты и старый архив, разовые документы — в legacy/ (#678)
Волна 1 эпика #674 — мёртвое без риска, каждое имя проверено git grep по текущему dev. Удалено: шесть demo/shot_*.mjs без читателей (shot_furniture ещё и не работает с #159), demo/capture_wall_strip_backup.mjs, demo/benchmark_coordinate_write_barrier.mjs (нигде не запускался, но входил в манифест smoke через demo/benchmark_*.mjs — теперь лист покрытия чист), scripts/dev/styles-split.mjs (падает на текущем src/styles.ts), docs/README.ru.md (индекс четырёх документов из 38 — роль у README.ru.md), demo/README.md (одна строка в карте пакета AGENTS.md вместо него) и из legacy/ — снимок аудита v1.58.0, черновик 089, завершённые планы, продуктовые снимки и две разовые диагностики; история git хранит. legacy/docs/SUN-CONTRAST.md остаётся: на него ссылаются src/sun.ts и SUN.md. Перенесено в legacy/docs/: superpowers/specs (11 дизайн-документов до процесса), QUALITY-560.md, ROADMAP.md, STATUS-FEATURES.md. Ссылки поправлены там, где они были: FILTERING.md, ТЗ 006/007/058, testing-notes, комментарии src/open-spans.ts и validation.py (только путь документа), SCOPE.md, STATUS.md, smoke_household_journeys.mjs, опись legacy/README.md; ложный маркер benchmark_coordinate_write_barrier снят с чек-листа. Issue: #678 User-Visible: no Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
This commit is contained in:
@@ -1,245 +0,0 @@
|
||||
/**
|
||||
* #266: one-shot mechanical splitter. Reads src/styles.ts, cuts the css``
|
||||
* template into top-level blocks (rule, @media, @keyframes — with their
|
||||
* leading comments), classifies every block by the owner of its first
|
||||
* selector token, and writes five surface files plus the aggregator.
|
||||
* A token missing from the table aborts the run: no silent "misc" bucket.
|
||||
*/
|
||||
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
||||
|
||||
const ZONES = { base: [], plan: [], devices: [], chrome: [], dialogs: [] };
|
||||
const T = {};
|
||||
const assign = (zone, tokens) => { for (const t of tokens.split(/\s+/)) T[t] = zone; };
|
||||
|
||||
assign('base', ':host .sr-only ha-card .bootveil .toast .empty .spacer');
|
||||
assign('plan', `.stage .plan-svg .hp-paper .hp-static-stage .hp-day-cycle-bg .hp-day-cycle-sun .hp-day-cycle-env
|
||||
.seg .vertex .pathline .preview .active-axis .active-vertex .physical-hit .physical-chrome .physical-drag
|
||||
.drawwall .drawwall-preview .drawwall-preview-fill .zero-wall .wall-repair-preview
|
||||
.wallbody .wallbody-fill .wallthick-hover .griddot
|
||||
.plan-snap-node .plan-snap-line .plan-snap-overlay .hidden-wall-line .hidden-wall-node .hidden-wall-diagnostic
|
||||
.room .room-outline .room-hover-fill .room-hover-halo .room-hover-outline .roomlabel
|
||||
.rlhandle .rlgear .rlgearbtn .rlname .rlgo
|
||||
.measurelabel .measurelayer .compass .homearrow .zoomwrap .zoomctl .zoombadge
|
||||
.decorlayer .glow-spot .glow-pools .glow-pools-frame .glow-base-layer .glow-base-tunnels .sunlayer
|
||||
.iso-opening-panel .iso-ambient-shadow .iso-wall-side .iso-wall-top
|
||||
.iso-floor-side .iso-side-hi .iso-side-lo .iso-top-hi .iso-top-lo .iso-shadows-svg .iso-underlay-svg
|
||||
.iso-walls-svg .iso-underlay .iso-shadows .iso-walls .iso-openings
|
||||
.opening-preview .opening-preview-dot .opening-dimension .opening-dimensions .opening-dimension-line
|
||||
.opening-dimension-tick .op-leaf .op-arc .op-hit .op-outline .passage-preview-boundary .passage-preview-cut
|
||||
.aligndot .alignline .alignmsg .rszhandle .rszicon .rszleader .rszhalo .rszink .rszmeasurehalo
|
||||
.rszmeasureink .rszmeasurelayer .bdframe .dtframe .dtarea .devlayer .fixedfloor-loading .fixedfloor-error
|
||||
line .projection-toggle`);
|
||||
assign('devices', `.dev .device-pulse .device-shell .device-shell-frame .device-core .device-sections
|
||||
.devicepreview-empty .activity-dot .vactrail .vacpuck .temprange .kdot .kioskdots`);
|
||||
assign('chrome', `.editbar .editbar-end .editbar-tools .tab .tabs .modetab .modes .decorbar
|
||||
.editorchrome .editorchrome-inner .menu .menuwrap .dropbtn .droppanel .hdr .farhint .rhint .tip .togglehint`);
|
||||
assign('dialogs', `hp-dialog .btn .oplock .oplock-core .oplock-shell .rrow .colorrow .ripple-colorrow
|
||||
.ripple-sizerow .optimize-details .optimize-live .optimize-cleanup .optimize-selected .recoveryoverlay
|
||||
.savedplan .savedplans .savedmeta .backupdetails .backupactions .backupfile .backupcontent .backupplanonly
|
||||
.backupplanonlystatus .backupupload .backupchoices .backupconfirm .backuperror .backupsummary .backupwarn
|
||||
.backupcounts .backupbody .pdf .pdfedit .pdflist .pdftag .planprev .planrow .planname .cardpreview
|
||||
.namein .descin .areasel .entrow .entlist .entbtn .cand .candlist .srcrow .gsrow .inforow .infodesc
|
||||
.dispsection .floorrow .fileupload .filebtn .furnsize .furnhd .furnitem .furnbody .furngroup .furnhint
|
||||
.furnpalette .furnprev .furnrow .wallthick-dlg .vacpicker .vacsource .vacsource-list .vacsource-meta
|
||||
.vacsource-warning .vacdiag .vacfit .vacfitdot .vacfithandle .vacfitknob .vacbox .vaccalbar .vacxcme
|
||||
.sunrow .suncol .aboutlink .aboutver .count .head .title .markerhelplabel .markerhelpfield .markerradios
|
||||
.markeractions .markersaveactions .markerlightgroup .markerlightdisabled .markerglowvalue
|
||||
.markerbadgetechnical .opening-entity-candidate .opening-entity-empty .habindingbanner .bindsel
|
||||
.bindharow .curbind .ctrlchip .ctrlchips .ctrlopt .ctrlstate .ctrlstates .ctrllist .oprow .iconauto
|
||||
.rtest .rtesticon ha-icon-picker`);
|
||||
|
||||
const argValue = (name, dflt) => {
|
||||
const i = process.argv.indexOf(`--${name}`);
|
||||
return i === -1 ? dflt : process.argv[i + 1];
|
||||
};
|
||||
const sourcePath = argValue('source', 'src/styles.ts');
|
||||
// Cumulative slice list in the FINAL aggregator order suffix (#266 §1.4):
|
||||
// e.g. --take chrome,dialogs keeps base+plan+devices inline and appends the
|
||||
// two extracted files after the inline remainder.
|
||||
const TAKE = (argValue('take', 'base,plan,devices,chrome,dialogs') || '')
|
||||
.split(',').map((z) => z.trim()).filter(Boolean);
|
||||
const FINAL_ORDER = ['base', 'plan', 'devices', 'chrome', 'dialogs'];
|
||||
for (const zone of TAKE) if (!FINAL_ORDER.includes(zone)) throw new Error(`unknown zone ${zone}`);
|
||||
const source = readFileSync(sourcePath, 'utf8');
|
||||
const m = source.match(/css`([\s\S]*)`;\s*$/);
|
||||
if (!m) throw new Error('styles.ts: css template not found');
|
||||
const cssBody = m[1];
|
||||
|
||||
// Cut into top-level blocks with leading comments/blank lines attached.
|
||||
const blocks = [];
|
||||
let i = 0;
|
||||
let pending = '';
|
||||
while (i < cssBody.length) {
|
||||
const rest = cssBody.slice(i);
|
||||
const cm = rest.match(/^\s*\/\*[\s\S]*?\*\//);
|
||||
if (cm && !rest.slice(0, rest.indexOf('/*')).includes('{')) {
|
||||
// comment before any brace: attach to the next block
|
||||
pending += cm[0];
|
||||
i += cm[0].length;
|
||||
continue;
|
||||
}
|
||||
const open = cssBody.indexOf('{', i);
|
||||
if (open === -1) { pending += cssBody.slice(i); break; }
|
||||
const header = cssBody.slice(i, open);
|
||||
let depth = 1, j = open + 1;
|
||||
while (j < cssBody.length && depth > 0) {
|
||||
if (cssBody[j] === '{') depth++;
|
||||
else if (cssBody[j] === '}') depth--;
|
||||
j++;
|
||||
}
|
||||
blocks.push({ text: pending + header + cssBody.slice(open, j), header: header.trim() });
|
||||
pending = '';
|
||||
i = j;
|
||||
}
|
||||
|
||||
const zoneOfSelector = (header) => {
|
||||
// A leading :host(...) compound GATES a rule, it does not own it: the owner
|
||||
// is the first meaningful token after the gate. Classifying such groups
|
||||
// into base moved them AHEAD of their surface in the cascade and flipped
|
||||
// equal-specificity winners (smoke_device_icon_design caught alert shells
|
||||
// and dark-unavailable cores) — the owner surface keeps their position.
|
||||
const owner = (sel) => {
|
||||
let rest = sel.trim().replace(/^:host(\([^)]*\))?\s*/, '');
|
||||
if (!rest) return ':host';
|
||||
const t = rest.match(/^[.:#\[]?[\w-]+/);
|
||||
return t ? t[0] : null;
|
||||
};
|
||||
const zones = new Set();
|
||||
for (const sel of header.split(',')) {
|
||||
const token = owner(sel);
|
||||
if (!token) continue;
|
||||
const zone = T[token];
|
||||
if (!zone) throw new Error(`unclassified token «${token}» in «${header.slice(0, 80)}»`);
|
||||
zones.add(zone);
|
||||
}
|
||||
if (!zones.size) throw new Error(`no tokens in «${header.slice(0, 80)}»`);
|
||||
return zones.size === 1 ? [...zones][0] : 'base';
|
||||
};
|
||||
|
||||
const innerHeader = (header) => header.replace(/^@(media|supports)[^{]*$/s, '').trim();
|
||||
for (const block of blocks) {
|
||||
let zone;
|
||||
if (/^@keyframes/.test(block.header)) {
|
||||
// keyframes are owned by their consumers; classify by name prefix
|
||||
const name = block.header.replace(/^@keyframes\s+/, '');
|
||||
zone = /^(hp-dev|dev|pulse|vac)/.test(name) ? 'devices'
|
||||
: /^(hp-spin|spin|fade|toast)/.test(name) ? 'base' : 'base';
|
||||
} else if (/^@(media|supports)/.test(block.header)) {
|
||||
// Classify by ALL inner rules. A single-zone wrapper moves whole; a mixed
|
||||
// one is split into consecutive per-zone copies of the wrapper so every
|
||||
// rule stays with its owner surface without crossing zone order.
|
||||
const openAt = block.text.indexOf('{');
|
||||
const inner = block.text.slice(openAt + 1, block.text.lastIndexOf('}'));
|
||||
const innerBlocks = [];
|
||||
{
|
||||
let k = 0, pend = '';
|
||||
while (k < inner.length) {
|
||||
const rest2 = inner.slice(k);
|
||||
const cm2 = rest2.match(/^\s*\/\*[\s\S]*?\*\//);
|
||||
if (cm2 && !rest2.slice(0, rest2.indexOf('/*')).includes('{')) {
|
||||
pend += cm2[0]; k += cm2[0].length; continue;
|
||||
}
|
||||
const o2 = inner.indexOf('{', k);
|
||||
if (o2 === -1) break;
|
||||
const h2 = inner.slice(k, o2);
|
||||
let d2 = 1, j2 = o2 + 1;
|
||||
while (j2 < inner.length && d2 > 0) {
|
||||
if (inner[j2] === '{') d2++;
|
||||
else if (inner[j2] === '}') d2--;
|
||||
j2++;
|
||||
}
|
||||
innerBlocks.push({ text: pend + h2 + inner.slice(o2, j2), header: h2.trim() });
|
||||
pend = '';
|
||||
k = j2;
|
||||
}
|
||||
}
|
||||
const innerZones = innerBlocks.map((b) => zoneOfSelector(b.header));
|
||||
const uniq = [...new Set(innerZones)];
|
||||
if (uniq.length <= 1) {
|
||||
zone = uniq[0] ?? 'base';
|
||||
} else {
|
||||
const wrapperHeader = block.text.slice(0, openAt).trimEnd();
|
||||
let g = 0;
|
||||
while (g < innerBlocks.length) {
|
||||
const zg = innerZones[g];
|
||||
let h = g;
|
||||
while (h < innerBlocks.length && innerZones[h] === zg) h++;
|
||||
const bodyPart = innerBlocks.slice(g, h).map((b) => b.text).join('');
|
||||
const copy = `${wrapperHeader} {${bodyPart}\n }`;
|
||||
ZONES[zg].push(copy);
|
||||
g = h;
|
||||
}
|
||||
// mark handled: skip the shared push below
|
||||
block.zone = 'SPLIT';
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
zone = zoneOfSelector(block.header);
|
||||
}
|
||||
ZONES[zone].push(block.text);
|
||||
block.zone = zone;
|
||||
}
|
||||
|
||||
mkdirSync('src/styles', { recursive: true });
|
||||
const taken = FINAL_ORDER.filter((zone) => TAKE.includes(zone));
|
||||
const kept = FINAL_ORDER.filter((zone) => !TAKE.includes(zone));
|
||||
const NAMES = { base: 'base', plan: 'plan', devices: 'devices', chrome: 'chrome', dialogs: 'dialogs' };
|
||||
const DOC = {
|
||||
base: 'Host, variables, resets and cross-surface rules',
|
||||
plan: 'The plan scene: stage, walls, axes, snap, decor, iso and resize ink',
|
||||
devices: 'Device markers, shells, pulses and vacuum presentation',
|
||||
chrome: 'Editor chrome: toolbars, tabs, menus and hints',
|
||||
dialogs: 'Dialogs, forms, buttons and pickers',
|
||||
};
|
||||
const tidy = (chunk) => chunk.replace(/^\n+/, '').replace(/\n+$/, '');
|
||||
for (const zone of taken) {
|
||||
const body = ZONES[zone].map(tidy).join('\n');
|
||||
writeFileSync(`src/styles/${NAMES[zone]}.styles.ts`,
|
||||
`/** ${DOC[zone]} (#266, split from styles.ts). */\nimport { css } from 'lit';\n\nexport const ${zone}Styles = css\`\n${body}\n\`;\n`);
|
||||
console.log(zone, ZONES[zone].length, 'blocks ->', `src/styles/${NAMES[zone]}.styles.ts`);
|
||||
}
|
||||
if (kept.length) {
|
||||
// Intermediate slice: the not-yet-extracted zones stay inline IN SOURCE
|
||||
// ORDER, extracted zones append after them in the final-order suffix, so
|
||||
// every relative position matches the final aggregator and golden can gate
|
||||
// each slice.
|
||||
const keptBlocks = blocks.filter((block) => !taken.includes(block.zone));
|
||||
const imports = taken.map((zone) =>
|
||||
`import { ${zone}Styles } from './styles/${NAMES[zone]}.styles';`).join('\n');
|
||||
writeFileSync('src/styles.ts', `/** Styles of the House Plan card — being split by surface (#266). */
|
||||
import { css } from 'lit';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
${imports}
|
||||
|
||||
export { ${taken.map((zone) => `${zone}Styles`).join(', ')} };
|
||||
|
||||
const inlineStyles = css\`
|
||||
${keptBlocks.map((block) => tidy(block.text)).join('\n')}
|
||||
\`;
|
||||
|
||||
export const cardStyles: CSSResultGroup = [
|
||||
inlineStyles, ${taken.map((zone) => `${zone}Styles`).join(', ')},
|
||||
];
|
||||
`);
|
||||
console.log('aggregator with inline remainder written:', kept.join('+'), 'inline;', taken.join(', '), 'extracted');
|
||||
} else writeFileSync('src/styles.ts', `/**
|
||||
* Styles of the House Plan card — assembled from the surface files (#266).
|
||||
*
|
||||
* The ORDER of this array is part of the cascade contract: rules of equal
|
||||
* specificity resolve by position, and the golden set was accepted against
|
||||
* exactly this order. Do not reorder without re-reviewing the golden set.
|
||||
*/
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
import { baseStyles } from './styles/base.styles';
|
||||
import { planStyles } from './styles/plan.styles';
|
||||
import { devicesStyles } from './styles/devices.styles';
|
||||
import { chromeStyles } from './styles/chrome.styles';
|
||||
import { dialogsStyles } from './styles/dialogs.styles';
|
||||
|
||||
export { baseStyles, planStyles, devicesStyles, chromeStyles, dialogsStyles };
|
||||
|
||||
export const cardStyles: CSSResultGroup = [
|
||||
baseStyles, planStyles, devicesStyles, chromeStyles, dialogsStyles,
|
||||
];
|
||||
`);
|
||||
console.log('aggregator written');
|
||||
Reference in New Issue
Block a user