feat(a11y): image alt text via NIP-92 imeta across all post types - #746
Merged
Merged
Conversation
Read, display, and author alt text for images using NIP-92 imeta tags, matching the wire behavior of Amethyst and Gossip. Read: - parse imeta url->alt map (imetaAltByUrl) and render as img alt - ALT badge on media tiles opens a Description dialog - fullscreen viewer shows alt as a bottom caption - profile media grid and lightbox carry alt through Compose: - shared AltTextEditorModal (+ ALT / check-ALT chips, 2000-char cap, drafts persist alt keyed by URL) - wired into post composer, reply composer, recipe create/fork/gated (MediaUploader), longform cover, and marketplace product form - publish emits one imeta tag per described image; no empty metadata AI (Cook+): - ask-photo gains purpose:"alt" — neutral describer instruction, no food gate, temperature 0.4, member question ignored - Generate-with-AI action in the editor; membership + rate limits reuse the existing Cheffy photo pipeline Includes an Amethyst-published event locked in as an interop regression test and a mobile handoff spec in docs/accessibility/alt-text-imeta-handoff.md.
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved critical and moderate issues affect metadata preservation, draft persistence, rendering, and accessibility.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (9)
Map and Record mismatch breaks forked alt text · New Alt badges stack because media tiles lack positioned ancestors · New Nested controls make additional-media tiles inaccessible · New Editing listings strips existing image alt text · New Article cover alt text is not persisted by URL · New Article readers do not render cover alt text · New Marketplace product views discard image alt text · New Recipe readers ignore published image alt text · New Forking recipes discards existing imeta fields · New
What changed in this PR
Adds NIP-92 imeta image alt-text support across publishing, rendering, marketplace content, AI generation, and mobile handoff documentation.
Changes:
- Adds alt-text parsing, editing, badges, dialogs, captions, and lightbox support.
- Integrates alt-text publishing and draft handling across posts, recipes, articles, replies, and products.
- Adds Cook+ AI alt generation, tests, interoperability coverage, and documentation.
| File | Description |
|---|---|
src/routes/user/[slug]/+page.svelte |
Adds profile media alt text. |
src/routes/fork/[slug]/+page.svelte |
Preserves alt text when forking recipes. |
src/routes/create/gated/+page.svelte |
Emits gated recipe alt metadata. |
src/routes/create/+page.svelte |
Emits recipe alt metadata. |
src/routes/api/zappy/ask-photo/askPhoto.test.ts |
Tests AI alt mode. |
src/routes/api/zappy/ask-photo/+server.ts |
Implements alt-generation API mode. |
src/lib/photoAsk.ts |
Sends alt-generation requests. |
src/lib/noteReview.test.ts |
Updates accessibility assertions. |
src/lib/marketplace/types.ts |
Adds product alt-text state. |
src/lib/marketplace/products.ts |
Emits product imeta tags. |
src/lib/feed/imeta.ts |
Parses and builds alt metadata. |
src/lib/feed/imeta.test.ts |
Tests alt metadata interoperability. |
src/lib/cheffyPrompt.server.ts |
Adds neutral alt-text prompting. |
src/components/reads/LongformEditorModal.svelte |
Adds article cover alt editing. |
src/components/PostComposer.svelte |
Adds post alt editing and persistence. |
src/components/NoteContent.svelte |
Applies alt text to note media. |
src/components/MediaUploader.svelte |
Adds uploader alt controls. |
src/components/MediaLightbox.svelte |
Adds lightbox captions. |
src/components/MediaCarousel.svelte |
Adds alt badges and descriptions. |
src/components/marketplace/ProductForm.svelte |
Connects product alt state. |
src/components/FoodstrFeedOptimized.svelte |
Passes alt text into feed media. |
src/components/comments/ReplyComposer.svelte |
Adds reply alt editing. |
src/components/AltTextEditorModal.svelte |
Provides manual and AI alt editing. |
package.json |
Bumps the application version. |
docs/accessibility/alt-text-imeta-handoff.md |
Documents web/mobile alt-text integration. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…ork slots Resolves the Copilot review findings on zapcooking#746: - fork: imetaAltByUrl returns a Map; convert to the Record the editor reads. Preserve the source imeta row (m, dim, blurhash, x, fallback) and only replace the alt slot via new withImetaAlt/imetaTagsByUrl. - MediaCarousel: .media-tile is now position: relative so each ALT badge overlays its own tile instead of stacking on the gallery. - MediaUploader: additional-media tiles no longer nest buttons — the make-cover action is a sibling overlay button beside remove and alt. - Marketplace: parseProductEvent carries imeta alt into Product; the edit route seeds the form so saving doesn't strip it; ProductCard and ProductViewModal render it. - Longform: cover alt is persisted in ArticleDraft.coverAlts keyed by cover URL, included in the dirty-state hash, and read for the current URL on publish. - Recipe.svelte (recipes + article covers): carousel and lightbox use the imeta alt map with a visible caption in the lightbox. - Recipe drafts: RecipeDraft.imageAlts round-trips through edit (Recipe.svelte → /create) and draft save/load on create + gated. - AltTextEditorModal: fix maxLength prop errors flagged by svelte-check. svelte-check: 0 errors. vitest: 1994 passing (6 new imeta tests). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Brings the reader-side convention from the feed carousel to Recipe.svelte: images with imeta alt text show an ALT badge (a sibling of the image button, no nested controls) that opens the description. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Adds read + author support for image alt text using NIP-92
imetatags, matching the wire behavior Amethyst and Gossip already implement. Works kind-agnostic: notes, replies/comments, recipes (create/fork/gated), longform article covers, and marketplace products.Verified interop: parses and displays alt from real Amethyst-published notes (locked in as a regression test with a live relay event), and emits tags that Amethyst/Gossip/Damus render.
Read path
imetaAltByUrl()builds a url→alt map per event; alt renders asimg altacross feed, note detail, lightbox, and profile media gridCompose path
AltTextEditorModal("+ ALT" → "✓ ALT" chip flow, 2000-char cap, per-URL persistence in drafts)MediaUploader, longform cover, product formimetatag per described image; no empty metadata; newlines flattened; existing imeta slots preserved on fork/editAI generation (Cook+)
POST /api/zappy/ask-photogainspurpose: "alt": neutral describer instruction, no food gate (alt must describe any image), temperature 0.4, member question ignoredNOT_MEMBER→ upsell), and 8/hr + 30/day per-pubkey rate limits as Cheffy photo asksTests
Mobile handoff
docs/accessibility/alt-text-imeta-handoff.mdcontains the wire-format spec, UX conventions, AI endpoint contract, test vector, and a rollout checklist for the Android and iOS versions.