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

Describe each photo once: the words become its alt text on the listing, the property page, the email and the flyer #77

Merged opened this

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

Merged

Each photo can now carry a one-line description that becomes its `alt` text on the surfaces below. I couldn't run `bun x tsc --noEmit` in this sandbox: the repo has no `tsconfig.json` or TypeScript dependency, so the type check is untried. `bun test` gives 280 pass and 2 fail. One failure is `comingsoon.test.ts`, which I fixed and re-ran green. The other, "no lockfile or node_modules: nothing is i…

shipped production is serving it deploy 957b843

opened this

Each photo can now carry a one-line description that becomes its alt text on the surfaces below. I couldn't run bun x tsc --noEmit in this sandbox: the repo has no tsconfig.json or TypeScript dependency, so the type check is untried. bun test gives 280 pass and 2 fail. One failure is comingsoon.test.ts, which I fixed and re-ran green. The other, "no lockfile or node_modules: nothing is installed", I did not look into; I read it as coming from the node_modules that bun install created here. bun scripts/lint-design.ts printed nothing, which I read as clean.

What changed

  • Storage: a new photo_alt column on listing (a JSON map of photo name to description, default {}), added by ALTER TABLE so existing rows keep their data. The same migration describes the three sample listings that are already planted. New databases get the same descriptions from src/sample.ts.
  • Validation and saving: cleanAlt in src/photos.ts trims the text and caps it at 120 characters. It refuses control characters and anything that isn't text. updatePhotos takes alt, and a name that isn't on the listing is refused. A removed photo takes its description with it, and empty text clears one.
  • Doors: the Edit facts form has a "Describe photo N" box per photo, posted as alt.<name>. JSON PATCH { photos: { order, remove, alt } } and the MCP update_photos tool accept it, and contract/openapi.json is re-emitted.
  • Where it shows: a new altOf(listing, name, fallback) is used by the listing's photo strip, the flyer, the property page, the email (and so "Copy as email"), the square picture and the Edit facts strip. A photo without a description keeps its current alt.
  • Fair Housing: each description box on Edit facts runs check() and lists its findings beside the box. A flagged description is still saved, the same as flagged typed words.
  • Approvals: the flyer and email approvals now clear only when the photo list or its order changes. A save that only changes descriptions clears nothing, and the README says so. Before, any photo save cleared them, including a save that changed nothing.
  • Tests: src/photoalt.test.ts covers saved and shown on each surface, too long and bad input refused, and a hostile description escaped everywhere. It also covers the finding shown, approvals kept or cleared, removal, and the migration on existing rows. In src/comingsoon.test.ts I changed one expectation, because the migration replay now sees the new empty photo_alt column.

What I left alone

  • The home-list thumbnail keeps alt="", since it sits next to the address and is decorative.
  • The report and sign-in page heroes keep the address as their alt. The issue doesn't list those files.
  • No change to contract.json.

To try it: open a listing's Edit facts, type "Kitchen with quartz island" under a photo and save. Then check the property page, the email preview and the flyer. The sample listings already carry descriptions.

Closes #73

commented

Each photo can now carry a one-line description that becomes its alt text on the surfaces below. I couldn't run bun x tsc --noEmit in this sandbox: the repo has no tsconfig.json or TypeScript dependency, so the type check is untried. bun test gives 280 pass and 2 fail. One failure is comingsoon.test.ts, which I fixed and re-ran green. The other, "no lockfile or node_modules: nothing is installed", I did not look into; I read it as coming from the node_modules that bun install created here. bun scripts/lint-design.ts printed nothing, which I read as clean.

What changed

  • Storage: a new photo_alt column on listing (a JSON map of photo name to description, default {}), added by ALTER TABLE so existing rows keep their data. The same migration describes the three sample listings that are already planted. New databases get the same descriptions from src/sample.ts.
  • Validation and saving: cleanAlt in src/photos.ts trims the text and caps it at 120 characters. It refuses control characters and anything that isn't text. updatePhotos takes alt, and a name that isn't on the listing is refused. A removed photo takes its description with it, and empty text clears one.
  • Doors: the Edit facts form has a "Describe photo N" box per photo, posted as alt.<name>. JSON PATCH { photos: { order, remove, alt } } and the MCP update_photos tool accept it, and contract/openapi.json is re-emitted.
  • Where it shows: a new altOf(listing, name, fallback) is used by the listing's photo strip, the flyer, the property page, the email (and so "Copy as email"), the square picture and the Edit facts strip. A photo without a description keeps its current alt.
  • Fair Housing: each description box on Edit facts runs check() and lists its findings beside the box. A flagged description is still saved, the same as flagged typed words.
  • Approvals: the flyer and email approvals now clear only when the photo list or its order changes. A save that only changes descriptions clears nothing, and the README says so. Before, any photo save cleared them, including a save that changed nothing.
  • Tests: src/photoalt.test.ts covers saved and shown on each surface, too long and bad input refused, and a hostile description escaped everywhere. It also covers the finding shown, approvals kept or cleared, removal, and the migration on existing rows. In src/comingsoon.test.ts I changed one expectation, because the migration replay now sees the new empty photo_alt column.

What I left alone

  • The home-list thumbnail keeps alt="", since it sits next to the address and is decorative.
  • The report and sign-in page heroes keep the address as their alt. The issue doesn't list those files.
  • No change to contract.json.

To try it: open a listing's Edit facts, type "Kitchen with quartz island" under a photo and save. Then check the property page, the email preview and the flyer. The sample listings already carry descriptions.

Changes requested — before the latest push

Solid change overall: validation at the boundary (cleanAlt), escaping everywhere, approvals kept on description-only saves, migration + tests, no contract.json cell touched. One miss against the issue: box 2 names the home card, and the home list's card thumbnail still has alt="" (src/pages/home.ts:222), so a described hero is not its alt text there. Fix: use altOf(l, hero, "") on that thumb (the list query must carry photo_alt), and add a test that the home list shows the description. If you keep it decorative on purpose, say why in the README and the issue must be amended; as written, the issue asks for it. Everything else on the list is met.

commented

The home list's card thumbnail now uses the hero photo's description as its alt text. This was the one miss @sentinel found. The alt="" at src/pages/home.ts:222 is now altOf(l, hero, ""). With no description it stays empty, as before.

The list query is SELECT *, so it already carried photo_alt, and I didn't change it.

I added a test in src/photoalt.test.ts that saves a description on the hero, renders the home page from listListings, and checks the description appears as an alt. The 10 tests in that file pass.

Not run:

  • Type check: the repo has no tsconfig.json, so bun x tsc --noEmit just printed its help text and checked nothing.
  • Design lint: scripts/design-lint.ts doesn't exist in this clone (only scripts/lint-design.ts). I didn't run that one, so the src/web/ check hasn't been run.
  • Full suite: left to the forge's check on this push.

🤖 Generated with Claude Code

Approved

The earlier ask is met: the home card thumbnail now uses the hero photo description, with a test. The rest reads clean. alt names are checked against the listing, text goes through cleanAlt (120 characters, no control characters), and every alt is escaped. The save goes through updatePhotos and its author check, and no contract.json cell changed. Photos stay page, API and MCP only, as before, so the terminal gains no door here. Non-blocking: no tsconfig.json, so the type check was not run.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs