Public: anyone can read this; the crew can change it.

A look keeps its page's size: scrollWidth, scrollHeight, innerWidth and innerHeight are stored on it and read back #10205

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/10202-a-look-keeps-its-page-s-size-scrollwidth → main 1 commit fork

  • Merged. scout/10202-a-look-keeps-its-page-s-size-scrollwidth was merged into main as 48b97f4.

Review

Sign in to review this change.
5 files changed +106 −2
src/core/looks.ts +41 −2
@@ -109,6 +109,16 @@ export interface Look {
109109 readonly attachedBy: string | null;
110110 /** The one line that says what an attached look shows. */
111111 readonly note: string | null;
112 /** The page's own size when it was pictured; absent when it was not kept. */
113 readonly size?: LookSize;
114 }
115
116 /** The document's scroll size and the window's inner size, in CSS pixels. */
117 export interface LookSize {
118 readonly scrollWidth: number;
119 readonly scrollHeight: number;
120 readonly innerWidth: number;
121 readonly innerHeight: number;
112122 }
113123
114124 interface Row {
@@ -125,6 +135,10 @@ interface Row {
125135 taken_as: "owner" | "nobody";
126136 attached_by: string | null;
127137 note: string | null;
138 scroll_width: number | null;
139 scroll_height: number | null;
140 inner_width: number | null;
141 inner_height: number | null;
128142 }
129143
130144 /**
@@ -140,6 +154,21 @@ const stored = (page: string, theme: LookTheme): string =>
140154 const unstored = (page: string): string =>
141155 page.startsWith(DARK_PREFIX) ? page.slice(DARK_PREFIX.length) : page;
142156
157 const sizeOf = (r: Row): { size: LookSize } | undefined =>
158 r.scroll_width === null ||
159 r.scroll_height === null ||
160 r.inner_width === null ||
161 r.inner_height === null
162 ? undefined
163 : {
164 size: {
165 scrollWidth: r.scroll_width,
166 scrollHeight: r.scroll_height,
167 innerWidth: r.inner_width,
168 innerHeight: r.inner_height,
169 },
170 };
171
143172 const toLook = (r: Row): Look => ({
144173 id: r.id,
145174 appId: r.app_id,
@@ -154,6 +183,7 @@ const toLook = (r: Row): Look => ({
154183 takenAs: r.taken_as,
155184 attachedBy: r.attached_by,
156185 note: r.note,
186 ...sizeOf(r),
157187 });
158188
159189 export interface RecordLookInput {
@@ -170,6 +200,8 @@ export interface RecordLookInput {
170200 /** A person's attachment: who, and what it shows. */
171201 readonly attachedBy?: string | undefined;
172202 readonly note?: string | undefined;
203 /** What the page reported of its size; none stores nulls. */
204 readonly size?: LookSize | undefined;
173205 }
174206
175207 /** Record a picture, or the reason there is none. A retake replaces. */
@@ -177,10 +209,13 @@ export function recordLook(db: Database, input: RecordLookInput): Look {
177209 const page = input.page ?? DEFAULT_PAGE;
178210 const theme = input.theme ?? DEFAULT_THEME;
179211 db.query(
180 `INSERT INTO look (id, app_id, env, commit_oid, page, kind, path, error, taken_as, attached_by, note, theme)
181 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)
212 `INSERT INTO look (id, app_id, env, commit_oid, page, kind, path, error, taken_as, attached_by, note, theme,
213 scroll_width, scroll_height, inner_width, inner_height)
214 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16)
182215 ON CONFLICT (app_id, env, commit_oid, page, kind) DO UPDATE SET
183216 path = excluded.path, error = excluded.error, taken_as = excluded.taken_as,
217 scroll_width = excluded.scroll_width, scroll_height = excluded.scroll_height,
218 inner_width = excluded.inner_width, inner_height = excluded.inner_height,
184219 created_at = strftime('%Y-%m-%dT%H:%M:%fZ','now')`,
185220 ).run(
186221 newId(),
@@ -195,6 +230,10 @@ export function recordLook(db: Database, input: RecordLookInput): Look {
195230 input.attachedBy ?? null,
196231 input.note ?? null,
197232 theme,
233 input.size?.scrollWidth ?? null,
234 input.size?.scrollHeight ?? null,
235 input.size?.innerWidth ?? null,
236 input.size?.innerHeight ?? null,
198237 );
199238 return getLook(db, input.appId, input.env, input.commitOid, page, input.kind, theme)!;
200239 }
src/core/migration-pins/20261109180000-a-look-keeps-its-page-size.pin added +1
@@ -0,0 +1,1 @@
1 3019cfe53c7084e72513fa38efece1d492752c1129e6717962db12a6e7f345dd
src/core/migrations/20261109180000-a-look-keeps-its-page-size.ts added +10
@@ -0,0 +1,10 @@
1 // A look keeps the size its page reported (#10202): the document's scroll
2 // size and the window's inner size, so "does this page scroll sideways" is
3 // answered from the row. Nullable, so every look from before reads as it
4 // was: no size. Additive.
5 export default `
6 ALTER TABLE look ADD COLUMN scroll_width INTEGER;
7 ALTER TABLE look ADD COLUMN scroll_height INTEGER;
8 ALTER TABLE look ADD COLUMN inner_width INTEGER;
9 ALTER TABLE look ADD COLUMN inner_height INTEGER;
10 `;
src/looks/service.ts +1
@@ -475,6 +475,7 @@ export function startLooks(app: App, options: LooksOptions): LooksHandle {
475475 kind,
476476 path,
477477 takenAs: asNobody(kind, page) ? "nobody" : "owner",
478 ...(shot.ok && shot.value.size !== undefined ? { size: shot.value.size } : {}),
478479 });
479480 } else {
480481 rmSync(staged, { force: true });
test/look-page-size.test.ts added +53
@@ -0,0 +1,53 @@
1 import { expect, test } from "bun:test";
2 import { Database } from "bun:sqlite";
3 import { getLook, recordLook } from "../src/core/looks.ts";
4 import { migrationFiles } from "../src/core/schema.ts";
5
6 const SIZE = { scrollWidth: 390, scrollHeight: 2000, innerWidth: 390, innerHeight: 844 };
7 const base = { appId: "a", env: "main", commitOid: "c1", kind: "phone" as const, path: "p.png" };
8
9 const fresh = (upTo?: string): Database => {
10 const db = new Database(":memory:");
11 for (const m of migrationFiles) {
12 if (upTo !== undefined && m.name >= upTo) break;
13 db.exec(m.sql);
14 }
15 return db;
16 };
17
18 test("a look keeps the size it was recorded with", () => {
19 const db = fresh();
20 recordLook(db, { ...base, size: SIZE });
21 expect(getLook(db, "a", "main", "c1", "/", "phone", "light")?.size).toEqual(SIZE);
22 });
23
24 test("a look without a size stores nulls and reads back with no size", () => {
25 const db = fresh();
26 const look = recordLook(db, base);
27 expect(look.size).toBeUndefined();
28 expect("size" in look).toBe(false);
29 });
30
31 test("a retake without a size clears the old one", () => {
32 const db = fresh();
33 recordLook(db, { ...base, size: SIZE });
34 expect(recordLook(db, base).size).toBeUndefined();
35 });
36
37 test("a row from before the migration reads with no size", () => {
38 const db = fresh("20261109180000");
39 db.run(
40 `INSERT INTO look (id, app_id, env, commit_oid, page, kind, path) VALUES ('x','a','main','c1','/','phone','p.png')`,
41 );
42 db.exec(migrationFiles.find((m) => m.name.startsWith("20261109180000"))!.sql);
43 const look = getLook(db, "a", "main", "c1", "/", "phone", "light");
44 expect(look?.path).toBe("p.png");
45 expect(look?.size).toBeUndefined();
46 });
47
48 test("a row with only some columns set has no size", () => {
49 const db = fresh();
50 recordLook(db, base);
51 db.run("UPDATE look SET scroll_width = 1");
52 expect(getLook(db, "a", "main", "c1", "/", "phone", "light")?.size).toBeUndefined();
53 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs