docs(hygiene): свести дубли документов подсистем и снять устаревшее (#679)

Волна 2 эпика #674 — у каждого правила один дом, остальные места ссылаются.

DECOR-EDITOR.md ← BACKDROP.md + LIVE-TEXT.md: один документ с нумерованными
разделами (§3 подложка, §5 текст с живыми значениями), на которые теперь
указывают комментарии кода вместо несуществовавших «BACKDROP §2/§3»;
исправлено утверждение, что space-card не рисует декор (он рисует подложку
и картинки декора, но не фигуры, мебель и текст). LIGHT.md ← матрица
настроек света (перевод, тест назван явно: test/devices.test.mjs «issues
84/88»). DEVICE-PRESENTATION.md ← правила «что показывает маркер» из
FILTERING.md (порядок cover → light sources → device role, шторы,
медиаплееры); «в одном pull request» → «в одном коммите». CANVAS.md: §9.5
«Оптимизировать планы» → CONFIG-COMPATIBILITY.md, overlay и планарные грани
Walls → WALL-THICKNESS.md §10–11, таблицы «было/стало» сняты. TESTING-DEMO.md
→ demo/stand/README.md: карта демо-дома и «чего на стенде нет», ручной
чек-лист снят (ручной фазы в процессе нет). ISOMETRIC.md — только текущее;
история Stage 2/4 — docs/adr/570-isometric-stage4-visual-handoff.md.
SUN.md: удалённый контракт фона снят, правило бумаги — в текущем разделе.
UX-MODES.md: декор над заливками, а не «под комнатами»; «hidden isometric»;
follow-up из #3 — все выпущены. Шапки VACUUM, WARM-REMOUNT («Выровнять всё
по сетке» → «Оптимизировать планы»), WALL-THICKNESS, STYLING-HOOKS,
CONFIG-COMPATIBILITY (#33), PDF-EXPORT — без устаревших статусов и планов.
README EN/RU: абзац про пересъёмку скриншотов → CONTRIBUTING.md, RADAR и
PDF-EXPORT в списке документации, RU догнал EN (2.5D, повторное
использование загруженного изображения, STAIRS). Один список канонических
документов подсистем в AGENTS.md и промпте ревьюера (_process.yml).
WALL-THICKNESS.md ссылается на ADR 282.

Правки src/** и validation.py — только пути документов в комментариях.

Issue: #679
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:
Claude
2026-09-27 18:07:37 +00:00
committed by claude[bot]
parent f989fbfe3d
commit 5a258f3128
48 changed files with 1229 additions and 1634 deletions
+10 -10
View File
@@ -887,7 +887,7 @@ export class HouseplanCard extends LitElement {
* command for an explicitly selected object. */
private _decorEraseConfirm: { id: string; kind: DecorShape['kind'] } | null = null;
/** The text dialog. Live references are part of `text`; `pickerEntity` is
* only transient UI state and is never persisted (docs/LIVE-TEXT.md). */
* only transient UI state and is never persisted (docs/DECOR-EDITOR.md §5). */
private _decorTextDialog: {
id?: string; x: number; y: number; text: string; color: string;
opacity: number; angle: string; sizeCm: number;
@@ -953,7 +953,7 @@ export class HouseplanCard extends LitElement {
moved: boolean;
} | null = null;
/**
* The live backdrop gesture (docs/BACKDROP.md §2): moving the picture by its
* The live backdrop gesture (docs/DECOR-EDITOR.md §3.2): moving the picture by its
* body, scaling it by a corner handle or rotating it by the upper handle.
* `base` is the untransformed, centred rectangle the transform is measured
* from, so a gesture never accumulates rounding of its own.
@@ -3782,7 +3782,7 @@ export class HouseplanCard extends LitElement {
s += (x.id || '') + ',' + (x.plan_aspect || '') + ',' + (x.plan_url || '').length + ','
// the backdrop transform is geometry: without it in the key a drag of
// the picture would leave the memoized model (and the content frame
// built from it) showing the old rectangle (docs/BACKDROP.md §5)
// built from it) showing the old rectangle (docs/DECOR-EDITOR.md §3.2)
+ (x.plan_x ?? '') + ',' + (x.plan_y ?? '') + ',' + (x.plan_scale ?? '') + ','
+ (x.plan_scale_x ?? '') + ',' + (x.plan_scale_y ?? '') + ',' + (x.plan_angle ?? '') + ','
+ (x.rooms?.length || 0) + ',' + (x.openings?.length || 0) + ',' + (x.decor?.length || 0) + ';';
@@ -5913,7 +5913,7 @@ export class HouseplanCard extends LitElement {
// far away in the Plan editor kept View framing the empty ground it left
// behind, until some unrelated model change happened to invalidate memo.
const grow = this._mode !== 'view';
// A LIVE BACKDROP GESTURE FREEZES THE FRAME (docs/BACKDROP.md §2). The
// A LIVE BACKDROP GESTURE FREEZES THE FRAME (docs/DECOR-EDITOR.md §3.2). The
// picture is a content item, so dragging it grows the frame — which
// rescales the view, which changes how many plan units a screen pixel is
// worth, mid-gesture: the picture then runs away from the finger and no
@@ -8172,7 +8172,7 @@ export class HouseplanCard extends LitElement {
}
// ---- common decor transform controller ----
// The mechanics are the backdrop frame's (docs/BACKDROP.md), reused
// The mechanics are the backdrop frame's (docs/DECOR-EDITOR.md §3), reused
// rather than reinvented: chrome that never takes a pointer, finger-sized
// handles that always do, the gesture written live into the config and
// PERSISTED only if something actually moved. What differs is the pivot —
@@ -8229,7 +8229,7 @@ export class HouseplanCard extends LitElement {
return this._editorRuntimeOrThrow()._confirmDecorErase();
}
// ============ backdrop transform frame (docs/BACKDROP.md) ============
// ============ backdrop transform frame (docs/DECOR-EDITOR.md §3) ============
/** The centred, UNTRANSFORMED rectangle of the current backdrop image. */
private get _bdBase(): Rect | null {
@@ -8346,7 +8346,7 @@ export class HouseplanCard extends LitElement {
/**
* The common selected-object frame: line endpoints, or a dashed outline,
* four corner handles and one rotation handle. Same
* mechanics and same handle size as the backdrop frame (docs/BACKDROP.md
* mechanics and same handle size as the backdrop frame (docs/DECOR-EDITOR.md §3
* §2) — finger-sized in SCREEN terms, so it stays grabbable at any zoom —
* and it rides the block's own rotation, so the corners stay at the corners.
*/
@@ -8530,7 +8530,7 @@ export class HouseplanCard extends LitElement {
// The label is painted from the LIVE value on every render — the same
// `hass` the rest of the card reads, no polling of its own. Without an
// entity `liveText` gives the stored text back byte-for-byte, so a
// plain label is the plain label it always was (docs/LIVE-TEXT.md).
// plain label is the plain label it always was (docs/DECOR-EDITOR.md §5).
const fs = this._decorTextUnits(sh);
const frozenText = this._renderDeviceSnapshot?.facts.get(`decor:${this._space}:${sh.id}`);
const lines = decorTextLines(typeof frozenText === 'string' ? frozenText : liveText(
@@ -10897,7 +10897,7 @@ export class HouseplanCard extends LitElement {
preserveAspectRatio="xMidYMid meet">
<g class=${iso ? 'iso-floor-scene' : nothing}
transform=${iso ? isoFloorMatrixCss() : nothing}>
${''/* THE PAPER IS THE ROOMS (docs/BACKDROP.md §3, owner
${''/* THE PAPER IS THE ROOMS (docs/DECOR-EDITOR.md §3.3, owner
2026-08-04). Opaque shapes stop the scene background —
bg_color or the day-cycle environment — from bleeding through the
plan. They follow the ROOM CONTOURS and nothing else: one
@@ -11119,7 +11119,7 @@ export class HouseplanCard extends LitElement {
${this._markup && this._tool === 'resize' ? this._renderResizeLayer(view) : nothing}
${''/* editor chrome, not plan content: the backdrop frame sits on
top of everything the plan draws so its handles stay
grabbable (docs/BACKDROP.md §2). It exists only in the
grabbable (docs/DECOR-EDITOR.md §3.2). It exists only in the
backdrop editor, where rooms and devices are pointer-inert. */}
${this._renderBackdropFrame(view)}
${this._renderTextFrame(view)}
+5 -5
View File
@@ -3730,7 +3730,7 @@ public _decorPointerDown(ev: PointerEvent): boolean {
// between letters appear to erase only the selection outline.
if (t === 'select') this.host._decorSel = null;
// …and under its own tool the picture is grabbable by its body
// (docs/BACKDROP.md §2). Only INSIDE the image rect: press beside the
// (docs/DECOR-EDITOR.md §3.2). Only INSIDE the image rect: press beside the
// picture and the plane still pans with one finger.
if (this.host._bdMovable) {
const r = this.host._bdRect!;
@@ -4450,7 +4450,7 @@ public _furnPlace(raw: number[], free = false, pointerType = 'mouse'): void {
...decorStylePatch(this.host._decorStyle, false),
};
// a straight piece stores no angle at all, exactly as a straight label
// stores none (docs/LIVE-TEXT.md §3)
// stores none (docs/DECOR-EDITOR.md §5.3)
if (placement.angle) shape.angle = placement.angle;
sp.decor = [...this.host._decorList, shape];
this.host._decorSel = id;
@@ -4797,7 +4797,7 @@ public _bdUp(): void {
public _renderBackdropFrame(view: { x: number; y: number; w: number; h: number }): TemplateResult | typeof nothing {
const r = this.host._bdRect;
if (!this.host._bdActive || !r) return nothing;
// Two radii, one gesture — the split the text frame uses (docs/LIVE-TEXT.md
// Two radii, one gesture — the split the text frame uses (docs/DECOR-EDITOR.md §5
// §3) and, since 2026-08-05, every corner handle in the card. `hr` is the
// HIT radius: a fraction of the visible view, so the target stays
// finger-sized at any zoom. `kr` is what you SEE — a quarter of it, because
@@ -5066,7 +5066,7 @@ public _renderEditorSecondary(): TemplateResult | typeof nothing {
public _renderDecorBar(): TemplateResult {
const tools = [
['select', 'mdi:cursor-default-outline', 'decor.select'],
// moving the picture is a TOOL (docs/BACKDROP.md §2) — offered only when
// moving the picture is a TOOL (docs/DECOR-EDITOR.md §3.2) — offered only when
// there IS a picture, so a hand-drawn space's bar is unchanged
...(this.host._bdRect ? [['backdrop', 'mdi:image-move', 'decor.backdrop'] as const] : []),
['line', 'mdi:vector-line', 'decor.line'],
@@ -8147,7 +8147,7 @@ public async _saveSpaceDialog(): Promise<void> {
// (the uploaded file stays on disk; only the reference is cleared).
// Its transform goes with it — there is nothing left for plan_x/plan_y/
// plan_scale to describe, and a stale one would silently apply to the
// NEXT picture uploaded here (docs/BACKDROP.md §1).
// NEXT picture uploaded here (docs/DECOR-EDITOR.md §3 §1).
if (d.source === 'draw') {
sp.plan_url = null; sp.plan_aspect = null;
delete sp.plan_x; delete sp.plan_y; delete sp.plan_scale;
+2 -2
View File
@@ -979,7 +979,7 @@ export function floorsOf(hass: any): FloorInfo[] {
return list;
}
// ---------------- live text on a decor label (docs/LIVE-TEXT.md) -------------
// ---------------- live text on a decor label (docs/DECOR-EDITOR.md §5) -------------
/** What a dead sensor says. A label that vanishes with its entity is worse
* than one that admits it has no data. */
@@ -1149,7 +1149,7 @@ export function valueWithUnit(v: HassValue, own: string, explicit?: string | nul
/**
* The live value of a linked label, unit included — formatted the way HOME
* ASSISTANT formats it (docs/LIVE-TEXT.md §2.1, docs/STYLING-HOOKS.md §6).
* ASSISTANT formats it (docs/DECOR-EDITOR.md §5.2, docs/STYLING-HOOKS.md §6).
*
* We still write no rounding logic of our own: the value goes through
* `hassValue`, which hands it to HA's formatter, so `display_precision`, the
+3 -3
View File
@@ -56,12 +56,12 @@ export function fitInSquare(ratio: number | null | undefined, side: number) {
return { x: (side - w) / 2, y: (side - h) / 2, w, h };
}
/** Per-axis backdrop scale bounds — mirrors validation.py (docs/BACKDROP.md). */
/** Per-axis backdrop scale bounds — mirrors validation.py (docs/DECOR-EDITOR.md §3). */
export const PLAN_SCALE_MIN = 0.01;
export const PLAN_SCALE_MAX = 100;
/**
* WHERE THE BACKDROP IMAGE SITS (docs/BACKDROP.md).
* WHERE THE BACKDROP IMAGE SITS (docs/DECOR-EDITOR.md §3).
*
* `fitInSquare` is only the DEFAULT placement: the image centred in the square
* canvas at its own proportions. On top of it a space may carry an optional
@@ -330,7 +330,7 @@ export function contentItems(
if (item) out.push(item);
}
// The backdrop image is ONE OF the objects of the space, exactly like a room
// (docs/BACKDROP.md §4): cropping to the outlined rooms would hide the parts
// (docs/DECOR-EDITOR.md §3 §4): cropping to the outlined rooms would hide the parts
// of the picture nobody has drawn over yet, and — since v1.58.0 — the
// rectangle here is the MOVED and SCALED one, so «Вписать всё» follows the
// picture wherever the owner has dragged it.
+1 -1
View File
@@ -666,7 +666,7 @@ export function renderSpaceStatic(o: StaticRenderOpts): TemplateResult | null {
? resolveDayCycle(planHass, o.dayCycleNow ?? new Date()) : null;
const stageBg = stageBgOf(o.cfg?.settings, disp);
// Opaque plan paper, same contract as the full card (docs/BACKDROP.md §3):
// Opaque plan paper, same contract as the full card (docs/DECOR-EDITOR.md §3.3):
// the paper is ALWAYS the ROOM CONTOURS and only them — never their bounding
// box, and (since v1.58.0) never the backdrop image rect either. The scene
// colour therefore reaches the exterior walls of an L-shaped house, fills the
+1 -1
View File
@@ -888,7 +888,7 @@ export const planStyles = css`
outline: 2px solid #26a69a;
outline-offset: -2px;
}
/* backdrop transform frame (docs/BACKDROP.md §2). Editor chrome: the
/* backdrop transform frame (docs/DECOR-EDITOR.md §3.2). Editor chrome: the
outline never takes a pointer, the four corner handles do — and they are
finger-sized (r = 2 % of the visible view), because this is dragged on a
tablet as often as with a mouse. */