mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-04 21:58:56 +00:00
feat(furniture): пакет 0.4.0 — 60 дизайнерских символов вместо 44 плюс 12 примитивов
Встроенная библиотека мебели была смешанной: 44 предмета рисовал дизайнер, а 12 — код, прямоугольниками и эллипсами в unit box. Они выбивались из стиля, и три категории меню (`computer`, `hood`, `oven`) вообще не открывались, потому что вида сверху для них не было. Пакет 0.4.0 перерисовывает все 56 существующих публичных ID и добавляет четыре новых: `computer`, `hood`, `oven`, `cactus`. Каталог становится равен поставке: 60 символов, 32 непустые категории. Совместимость. Ни один сохранённый план не мигрирует: положение, размер, поворот, отражение, цвет, прозрачность и толщина линии не трогаются, а default-размеры всех 56 прежних ID совпадают со старыми до числа — таблица зашита в `test/furniture.test.mjs`, чтобы сдвиг было видно тестом, а не глазами. Ловушка, ради которой примитивы удалены целиком. Библиотека собиралась союзом каталога и `LEGACY_FURNITURE`, и обе половины несли ОДНИ И ТЕ ЖЕ публичные ID. `BY_ID` — это `Map`, она оставляет последнюю запись, поэтому примитив молча побеждал дизайнерский рисунок, который его заменял: задача выглядит выполненной, 12 предметов не обновились, ни один существующий тест этого не видит. Поэтому `LEGACY_FURNITURE`, `RETAINED_IDS`, тип `Prim`, `box()` и `primitivePathD` удалены, а на дубликаты в каталоге стоит тест и мутант. Два продуктовых решения владельца (#593): - `cactus` живёт в категории `plant`, а не `exercise`, как предлагал пакет: плитка «Тренажёр» рисует тренажёр, и открывать её на кактус — врать о содержимом палитры. `exercise` остаётся скрытой пустой категорией, непустых становится 32, а не 33; - при отказе ленивого чанка с рисунками не рисуется НИЧЕГО. Прежде 12 примитивов рисовались всегда, и `docs/FURNITURE.md` это обещал; обещание снято. Это единственное место, где задача ухудшает наблюдаемое поведение, и оно проверяется смоком, а не словом. Бюджеты. Ревью ТЗ показало, что «бюджет ленивого графа защищён» было заявлением без гейта: размеры печатались в отчёт и ни с чем не сравнивались. Добавлены `lazyGraphCeilingViolation` и два настоящих потолка со своим мутантом. Стартовый граф ушёл ниже полосы, поэтому `INITIAL_VIEW_GZIP_CEILING` опущен 292 400 → 291 400 — вторая половина храповика, без которой выигрыш отыграется. Провенанс. Документ пакета воссоздан под 0.4.0: грант владельца, имя архива и его SHA-256. Это единственная исполнимая защита от повторения истории #159, и она не ослаблена — число проверок в тесте не уменьшилось, а мутант подменяет хеш и обязан её покраснить. Свидетели: пять новых мутантов, новая golden-сцена `furniture-new-symbols-light` для четырёх новых ID, матрица 63 → 64. Шестой мутант правится, а не добавляется: `furniture-placement-needs-art` ссылался на удалённое поле `g` и переставал компилироваться — раннер честно отличал это от «пойман» и отчитывался setup-failure. Ложь мутанта осталась прежней по смыслу, убрана только ветка про примитив. i18n: 7 ключей × 4 локали. `furn.cat_exercise` не добавлен — категория скрыта. Issue: #593 User-Visible: yes
This commit is contained in:
@@ -291,7 +291,7 @@ export const LOW_HEADROOM_WARNING_BYTES = 15_000;
|
||||
* оставлял под прежним центром 354 Б сверху — внутри шумовой полосы метрики.
|
||||
* Новый центр даёт 1 054 Б сверху и 946 Б до нижней границы полосы.
|
||||
*/
|
||||
export const INITIAL_VIEW_GZIP_CEILING = 292_400;
|
||||
export const INITIAL_VIEW_GZIP_CEILING = 291_400;
|
||||
export const INITIAL_VIEW_CEILING_BAND = 2_000;
|
||||
|
||||
/**
|
||||
@@ -373,6 +373,53 @@ export function initialViewCeilingViolation(bytes, {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* #593: потолки ленивых графов. До этой задачи их размеры только печатались в
|
||||
* отчёт, и заявление «бюджет ленивого графа защищён» было неправдой: сравнения
|
||||
* не существовало ни одного. Задача, которая перерисовывает всю библиотеку
|
||||
* мебели, живёт ровно в этих двух графах — значит она их и закрывает.
|
||||
*
|
||||
* Значения сняты с собранного бандла и поставлены с тем же запасом, что у
|
||||
* стартового графа: полоса 2000 Б и обязательный зазор больше 500 Б с обеих
|
||||
* сторон, чтобы ни семибайтный шум не красил гейт, ни молчаливый выигрыш не
|
||||
* оставался незафиксированным.
|
||||
*/
|
||||
export const LAZY_FURNITURE_ART_GZIP_CEILING = 17_900;
|
||||
export const LAZY_EDITOR_GZIP_CEILING = 222_900;
|
||||
export const LAZY_GRAPH_CEILING_BAND = 2_000;
|
||||
|
||||
/**
|
||||
* Потолок ленивого графа: `null`, пока значение внутри полосы.
|
||||
*
|
||||
* Отдельная функция, а не параметр `initialViewCeilingViolation`: текст отказа
|
||||
* обязан называть граф, иначе «graph 17 000 B выше потолка» не говорит, какой
|
||||
* именно граф вырос и куда смотреть.
|
||||
*/
|
||||
export function lazyGraphCeilingViolation(bytes, { ceiling, label, band = LAZY_GRAPH_CEILING_BAND } = {}) {
|
||||
if (!Number.isFinite(bytes)) {
|
||||
return { kind: 'missing', text: `${label} не измерен — потолок проверить нечем` };
|
||||
}
|
||||
if (bytes > ceiling) {
|
||||
return {
|
||||
kind: 'grew',
|
||||
over: bytes - ceiling,
|
||||
text: `${label} ${bytes} B gzip выше потолка ${ceiling} B на ${bytes - ceiling} B.`
|
||||
+ ' Ленивый граф грузится не в первом кадре, но он всё равно чей-то трафик:'
|
||||
+ ' поднимите потолок в этом же коммите, объяснив рост, либо уменьшите содержимое.',
|
||||
};
|
||||
}
|
||||
if (bytes < ceiling - band) {
|
||||
return {
|
||||
kind: 'shrank',
|
||||
under: ceiling - bytes,
|
||||
text: `${label} ${bytes} B gzip ниже потолка ${ceiling} B на ${ceiling - bytes} B`
|
||||
+ ` — больше полосы ${band} B. Опустите потолок: незафиксированный выигрыш`
|
||||
+ ' граф отыграет обратно.',
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Тревога о запасе: `null`, пока его хватает или пока долг признан. */
|
||||
export function lowHeadroomWarning(headroom, {
|
||||
threshold = LOW_HEADROOM_WARNING_BYTES,
|
||||
@@ -400,6 +447,8 @@ export function assertBundleBudget(
|
||||
manifest,
|
||||
budget = INITIAL_VIEW_GZIP_BUDGET,
|
||||
panelOnlyBudget = INITIAL_PANEL_ONLY_GZIP_BUDGET,
|
||||
lazyFurnitureArtCeiling = LAZY_FURNITURE_ART_GZIP_CEILING,
|
||||
lazyEditorCeiling = LAZY_EDITOR_GZIP_CEILING,
|
||||
) {
|
||||
assertBundleManifest(manifest);
|
||||
if (!manifest.lazyEditorFiles?.length) {
|
||||
@@ -449,6 +498,14 @@ export function assertBundleBudget(
|
||||
+ ` ${panelOnlyBudget} B budget`,
|
||||
);
|
||||
}
|
||||
// #593: два ленивых графа впервые становятся гейтом, а не строкой в отчёте.
|
||||
for (const [bytes, ceiling, label] of [
|
||||
[manifest.lazyFurnitureArtGzipBytes, lazyFurnitureArtCeiling, 'lazy furniture art graph'],
|
||||
[manifest.lazyEditorGzipBytes, lazyEditorCeiling, 'lazy editor graph'],
|
||||
]) {
|
||||
const violation = lazyGraphCeilingViolation(bytes, { ceiling, label });
|
||||
if (violation) throw new Error(violation.text);
|
||||
}
|
||||
return {
|
||||
initialViewGzipBytes: manifest.initialViewGzipBytes,
|
||||
initialPanelGzipBytes: manifest.initialPanelGzipBytes,
|
||||
@@ -477,7 +534,9 @@ if (import.meta.url === pathToFileURL(process.argv[1] || '').href) {
|
||||
`initial panel-only: ${result.initialPanelOnlyGzipBytes} B gzip`
|
||||
+ ` (budget ${INITIAL_PANEL_ONLY_GZIP_BUDGET} B,`
|
||||
+ ` headroom ${INITIAL_PANEL_ONLY_GZIP_BUDGET - result.initialPanelOnlyGzipBytes} B)`,
|
||||
`lazy editor: ${result.lazyEditorGzipBytes} B gzip`,
|
||||
`lazy editor: ${result.lazyEditorGzipBytes} B gzip (потолок ${LAZY_EDITOR_GZIP_CEILING} B ±${LAZY_GRAPH_CEILING_BAND})`,
|
||||
`lazy furniture art: ${result.lazyFurnitureArtGzipBytes} B gzip`
|
||||
+ ` (потолок ${LAZY_FURNITURE_ART_GZIP_CEILING} B ±${LAZY_GRAPH_CEILING_BAND})`,
|
||||
`lazy locale: ${result.lazyLocaleGzipBytes} B gzip`,
|
||||
`lazy isometric: ${result.lazyIsometricGzipBytes} B gzip`,
|
||||
`lazy PDF: ${result.lazyPdfGzipBytes} B gzip`,
|
||||
|
||||
Reference in New Issue
Block a user