mirror of
https://github.com/Matysh/houseplan-card
synced 2026-08-01 00:48:29 +00:00
v1.50.3: sizes are not coordinates (HP-1502-01)
The ±4 bound from v1.50.2 measured view_box[2:4] and room w/h with the same ruler as coordinates, so zero and negative sizes still passed the schema — and viewBox='0 0 0 0' draws nothing on every client, with the static card computing aspect-ratio: 0 / 0 on top. _EXTENT now requires strictly positive sizes with a floor of one thousandth of the canvas (1 render unit — far below any real room, keeps the maths finite); coordinates stay allowed to be negative, a crop origin legitimately sits past the edge. Defensive layer for stores that already hold a broken viewport: spaceModels falls back to the whole canvas — both cards render from that model, so both get the fallback — and a legacy rectangle with a negative size reads as the same rectangle drawn from the other corner. Also: the room settings button is the bottom row of the room card, and the room name renders in the same spot in view and plan modes (owner's request, committed earlier on dev).
This commit is contained in:
@@ -36,7 +36,7 @@ import { cardStyles } from './styles';
|
||||
import { fitInSquare, contentBounds } from './space-geometry';
|
||||
import { langOf, t, type I18nKey } from './i18n';
|
||||
|
||||
const CARD_VERSION = '1.50.2';
|
||||
const CARD_VERSION = '1.50.3';
|
||||
const LS_KEY = 'houseplan_card_layout_v1';
|
||||
const LS_CFG = 'houseplan_card_cfg_v1'; // cache of the server config+layout for instant rendering
|
||||
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
||||
|
||||
+30
-3
@@ -28,11 +28,37 @@ export type Pt = { x: number; y: number };
|
||||
export type Layout = Record<string, { s?: string; x: number; y: number } | undefined>;
|
||||
|
||||
/** Build render-space models (NORM_W × NORM_W) from a server config. */
|
||||
/** A stored view_box the render can trust: 4 finite numbers, positive sizes.
|
||||
* The server refuses anything else NOW (HP-1502-01), but a store may already
|
||||
* hold [0,0,0,0] or negative sizes from before — and a zero axis serialises
|
||||
* into viewBox="0 0 0 0", which draws nothing on every client. Bad input
|
||||
* falls back to the whole canvas rather than to a blank screen. */
|
||||
function safeViewBox(vb: any): [number, number, number, number] {
|
||||
if (
|
||||
Array.isArray(vb) && vb.length === 4 && vb.every((n: any) => Number.isFinite(n))
|
||||
&& vb[2] > 1e-6 && vb[3] > 1e-6
|
||||
) return vb as [number, number, number, number];
|
||||
return [0, 0, 1, 1];
|
||||
}
|
||||
|
||||
/** Legacy rectangle rooms, normalised: a negative size is the same rectangle
|
||||
* drawn from the other corner; the maths downstream assumes w/h >= 0. */
|
||||
function normRect(r: any): { x?: number; y?: number; w?: number; h?: number } {
|
||||
if (r.x == null || r.y == null) return { x: r.x, y: r.y, w: r.w, h: r.h };
|
||||
const w = Number(r.w) || 0, h = Number(r.h) || 0;
|
||||
return {
|
||||
x: w < 0 ? r.x + w : r.x,
|
||||
y: h < 0 ? r.y + h : r.y,
|
||||
w: Math.abs(w),
|
||||
h: Math.abs(h),
|
||||
};
|
||||
}
|
||||
|
||||
export function spaceModels(cfg: ServerConfig | null): SpaceModel[] {
|
||||
if (!cfg || !Array.isArray(cfg.spaces)) return [];
|
||||
return cfg.spaces.map((s: any) => {
|
||||
const H = NORM_W; // square canvas
|
||||
const scale = (r: any): RoomCfg => ({
|
||||
const scale = (raw: any): RoomCfg => { const r = { ...raw, ...normRect(raw) }; return {
|
||||
id: r.id,
|
||||
name: r.name,
|
||||
area: r.area ?? null,
|
||||
@@ -46,11 +72,12 @@ export function spaceModels(cfg: ServerConfig | null): SpaceModel[] {
|
||||
w: r.w != null ? r.w * NORM_W : undefined,
|
||||
h: r.h != null ? r.h * H : undefined,
|
||||
poly: r.poly ? r.poly.map((p: number[]) => [p[0] * NORM_W, p[1] * H]) : undefined,
|
||||
});
|
||||
}; };
|
||||
const vb = safeViewBox(s.view_box);
|
||||
return {
|
||||
id: s.id,
|
||||
title: s.title,
|
||||
vb: [s.view_box[0] * NORM_W, s.view_box[1] * H, s.view_box[2] * NORM_W, s.view_box[3] * H],
|
||||
vb: [vb[0] * NORM_W, vb[1] * H, vb[2] * NORM_W, vb[3] * H],
|
||||
bg: s.plan_url ? { href: contentUrl(s.plan_url), ...fitInSquare(s.plan_aspect, NORM_W) } : null,
|
||||
rooms: (s.rooms || []).map(scale),
|
||||
} as SpaceModel;
|
||||
|
||||
Reference in New Issue
Block a user