mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 13:18:58 +00:00
Three independent blind spots in the test harness. 1. smoke-select read symbols only from changed lines of a --unified=0 diff. An edit to the arguments of a multi-line call names nothing: #741 (d5bdfde9) changed only the arguments of runtime.resolveIsoOverlayFitEnvelope({ on the line above, and the selection answered "unproven" plus the visual minimum, although the callee is registered in smoke-links for smoke_iso_flat_parity and smoke_isometric_contract - the two smokes the #741 author ran by hand. The selection diff now carries CALL_CONTEXT_LINES = 3 lines of context; for each changed line parseDiff looks for the nearest unclosed "(" above it within the hunk, walking through a literal argument ({ or [ after "(", "," or "["), stopping at ";" on depth zero or any other unclosed brace. A callee from the symbol table joins symbols and the new callees field and is marked "(вызов)" in the report. Context lines never give direct symbols. task-packet takes a separate context diff for selectSmokes; change-risk keeps --unified=0. Over the last 80 src commits of dev: 16 commits gain a callee, 2 move from unproven to a proven link (#741, #7245f8e8ca7), +15 smokes in total, at most 4 per commit, none lost. 2. The #732 dead-field check judged only scene-builder calls. The four resolveIsoOverlayFitEnvelope({...}) literals in iso-scene-render tests went straight into the test-build function, so stageSize: null (the field #741 removed) stayed green. They now go through overlayFit typed with OverlayFitFixture (keys of IsoOverlayFitEnvelopeInput); the check judges overlayFit/resolveIsoOverlayFitEnvelope calls like the scene builders, and its probe asserts that OverlayFitFixture rejects stageSize, so the type resolved to the real input and not to any. 3. smoke_backdrop's mode() called the private _setMode and slept 220 ms. It now enters a mode through __hpTest.setMode and waits for the end of the transition by the same markers as section 6b (#715): one page helper used by both. Oracles and the 59 check names are unchanged. Witnesses:d5bdfde9selects both iso smokes with no "unproven"; the same fixture without context lines is unproven again; attribution disabled reds both AC1 units. stageSize: null in an overlayFit call reds the first #732 test; a direct resolveIsoOverlayFitEnvelope({...}) reds the third. smoke_backdrop is green normally and with animation frames slowed to 60 and 150 ms; a stage animation that never ends fails with a named error. Issue: #754 User-Visible: no Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
111 lines
5.9 KiB
Diff
111 lines
5.9 KiB
Diff
commit d5bdfde9196bc7ecf7a4cfefea04f4dd69559b0d
|
||
Author: Claude <noreply@anthropic.com>
|
||
Date: Thu Oct 1 08:25:09 2026 +0300
|
||
|
||
refactor(iso): drop the unused stageSize from the overlay fit input (#741)
|
||
|
||
Since #713 the overlay fit envelope reserves no nudge budget, and since
|
||
#725 _isoScene passes `stageSize: null` while resolveIsoOverlayFitEnvelope
|
||
never reads the field. The room focus still built a { width, height }
|
||
object from the stage for nothing. The optional field is removed from
|
||
IsoOverlayFitEnvelopeInput together with both call-site arguments.
|
||
|
||
The #725 AC3 unit compared bounds with stageSize null and 1000x500, which
|
||
is now meaningless; it checks instead that the fit bounds follow only
|
||
scene.frame and the tiles: the same bounds for every stage aspect, a moved
|
||
frame moves them, an enclosing frame is returned as is.
|
||
|
||
Issue: #741
|
||
User-Visible: no
|
||
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
|
||
|
||
diff --git a/src/houseplan-card.ts b/src/houseplan-card.ts
|
||
index b1421819..4ccce6a5 100755
|
||
--- a/src/houseplan-card.ts
|
||
+++ b/src/houseplan-card.ts
|
||
@@ -6004,7 +6004,7 @@ export class HouseplanCard extends LitElement {
|
||
disp, runtime.resolveIsoDecorationLayers(disp), scene, iconPct, effectiveDeviceBaseSize(iconPct),
|
||
disp.showLqi ?? this._config?.show_signal ?? true);
|
||
const envelope = overlays && runtime.resolveIsoOverlayFitEnvelope({
|
||
- baseBounds: scene.frame, entries: overlays.entries, stageSize: null, targetView: (bounds) =>
|
||
+ baseBounds: scene.frame, entries: overlays.entries, targetView: (bounds) =>
|
||
fitView([bounds.x, bounds.y, bounds.w, bounds.h], aspect) });
|
||
return envelope ? { ...scene, frame: envelope.bounds, overlayFitEntries: overlays.entries } : scene;
|
||
}
|
||
@@ -6192,8 +6192,7 @@ export class HouseplanCard extends LitElement {
|
||
const runtime = this._isoSceneRuntime, entries = this._renderIsoScene?.overlayFitEntries;
|
||
if (target && this._effectiveProjection() === 'iso' && runtime && entries?.length) {
|
||
const envelope = runtime.resolveIsoOverlayFitEnvelope({
|
||
- baseBounds: bounds, entries, ownerId: room.id, stageSize: {
|
||
- width: stage.clientWidth, height: stage.clientHeight },
|
||
+ baseBounds: bounds, entries, ownerId: room.id,
|
||
targetView: (fitBounds) => targetOf(fitBounds)?.viewBox ?? null });
|
||
if (envelope) { bounds = envelope.bounds; target = targetOf(bounds); }
|
||
}
|
||
diff --git a/src/iso-scene-render.ts b/src/iso-scene-render.ts
|
||
index cf61222b..a617fbb6 100644
|
||
--- a/src/iso-scene-render.ts
|
||
+++ b/src/iso-scene-render.ts
|
||
@@ -648,7 +648,6 @@ export function isoOverlaySceneBounds(
|
||
export interface IsoOverlayFitEnvelopeInput {
|
||
baseBounds: Rect;
|
||
entries: readonly IsoOverlayRenderEntry[];
|
||
- stageSize?: { width: number; height: number } | null;
|
||
targetView(bounds: Rect): Rect | null;
|
||
ownerId?: string;
|
||
}
|
||
diff --git a/test/iso-scene-render.test.mjs b/test/iso-scene-render.test.mjs
|
||
index e514aa8f..eecd468f 100644
|
||
--- a/test/iso-scene-render.test.mjs
|
||
+++ b/test/iso-scene-render.test.mjs
|
||
@@ -195,7 +195,7 @@ test('#713 K8: overlay fit is the structure plus visible tiles, no #651 nudge re
|
||
assert.deepEqual(otherRoom.bounds, { x: 0, y: 0, w: 100, h: 100 });
|
||
});
|
||
|
||
-test('#725 AC3: the overlay fit frame does not depend on the stage aspect or size', () => {
|
||
+test('#725 AC3/#741: the overlay fit bounds depend only on scene.frame and the tiles, never on the stage', () => {
|
||
/** @type {OverlayEntryFixture[]} */
|
||
const entries = [
|
||
{
|
||
@@ -213,25 +213,25 @@ test('#725 AC3: the overlay fit frame does not depend on the stage aspect or siz
|
||
},
|
||
},
|
||
];
|
||
- const baseBounds = { x: 0, y: 0, w: 100, h: 100 };
|
||
+ const frame = { x: 0, y: 0, w: 100, h: 100 };
|
||
// The card's targetView: a contain-fit of the frame at the stage aspect.
|
||
const fitAt = (aspect) => (bounds) => fitView([bounds.x, bounds.y, bounds.w, bounds.h], aspect);
|
||
- const results = [];
|
||
- for (const aspect of [0.5, 2]) {
|
||
- for (const stageSize of [null, { width: 1000, height: 500 }]) {
|
||
- const fitted = resolveIsoOverlayFitEnvelope({ baseBounds, entries, stageSize, targetView: fitAt(aspect) });
|
||
- assert.ok(fitted, `aspect ${aspect}, stage ${JSON.stringify(stageSize)}: an envelope`);
|
||
- results.push(fitted);
|
||
- }
|
||
- }
|
||
+ const fit = (baseBounds, aspect) => resolveIsoOverlayFitEnvelope({ baseBounds, entries, targetView: fitAt(aspect) });
|
||
+ // Portrait, square, a 1000×500 and a 320×180 stage, the frame's own aspect (`_isoScene`).
|
||
+ const aspects = [0.5, 1, 1000 / 500, 320 / 180, frame.w / frame.h];
|
||
+ const results = aspects.map((aspect) => {
|
||
+ const fitted = fit(frame, aspect);
|
||
+ assert.ok(fitted, `aspect ${aspect}: an envelope`);
|
||
+ assert.deepEqual(fitted.view, fitAt(aspect)(fitted.bounds), `aspect ${aspect}: the view fits the bounds`);
|
||
+ return fitted;
|
||
+ });
|
||
assert.deepEqual(results[0].bounds, { x: 0, y: 0, w: 105, h: 110 }, 'the structure plus both tiles');
|
||
for (const fitted of results) assert.deepEqual(fitted.bounds, results[0].bounds);
|
||
assert.notDeepEqual(results[0].view, results[2].view, 'only the view follows the aspect');
|
||
- // `_isoScene` passes the frame's own aspect: any positive finite value gives the same bounds.
|
||
- const ownAspect = resolveIsoOverlayFitEnvelope({
|
||
- baseBounds, entries, stageSize: null, targetView: fitAt(baseBounds.w / baseBounds.h),
|
||
- });
|
||
- assert.deepEqual(ownAspect.bounds, results[0].bounds);
|
||
+ // The bounds follow scene.frame: a moved frame moves them, an enclosing frame is the answer as is.
|
||
+ assert.deepEqual(fit({ x: 50, y: 0, w: 100, h: 100 }, 2).bounds, { x: 34, y: 0, w: 116, h: 110 });
|
||
+ const enclosing = { x: -50, y: -50, w: 300, h: 300 };
|
||
+ for (const aspect of aspects) assert.deepEqual(fit(enclosing, aspect).bounds, enclosing);
|
||
});
|
||
|
||
test('one painter queue paints a nearer wall after an unrelated rear opening', () => {
|