Spaces:
Running
viewer: name, describe and link every photo the search finds
Vấn đề
Ảnh nội thất mở trong viewer chỉ hiện đúng bức ảnh — không tiêu đề, không loại phòng, không danh mục, không link về bài gốc — dù mọi bản ghi đã có đủ cả bốn. worker.js:80 spread nguyên dòng image-embeddings.json vào mỗi kết quả tìm kiếm, nên title/categories/source đã tới tay viewer và chỉ đơn giản là chưa được render.
Thay đổi
1. Panel chi tiết (src/detail.js + src/PhotoViewer.jsx + src/index.css)
src/detail.js được khôi phục từ commit ffe2656 — chính commit bạn vừa yêu cầu revert — vốn mang file này cùng phần wiring trong viewer. Nó là bản port của detailOf trong share-seo.ts của vai-avatar, nên panel trong app và trang /share/ thống nhất về tên ảnh và loại phòng.
Panel hiển thị: tiêu đề, mô tả, tag phòng/chủ đề, bảng thông tin, link về nguồn. Dùng <details> nên thu gọn mặc định — trên điện thoại ảnh vẫn là thứ đầu tiên người dùng thấy.
2. Share URL dùng slug (src/assets.js + src/catalogue.js)
| trước | sau | |
|---|---|---|
| sản phẩm | /share/p/MH800TC |
/share/p/mh800tc-malloca-may-hut-mui-gan-tu-am-tu-80cm |
MH800TC không nói gì về thứ được chia sẻ; slug đọc ra tên sản phẩm — với cả người lẫn crawler. Slug vốn đã là khóa chuẩn trong products-gallery.json, và /share/p/ của vai-avatar khớp slug trước, nên cách hiển thị thay đổi, không phải khả năng resolve.
3. Sửa lỗi: 2/3 danh mục không có thông số
Đây là phát hiện khi kiểm thử, không nằm trong yêu cầu ban đầu:
8.053 / 11.704 dòng catalogue lưu specs dưới dạng Python repr — single quotes — nên JSON.parse từ chối và specsOf trả null. Kết quả: 2/3 cửa hàng hiện tên + giá nhưng không có thông số nào, im lặng không có dấu hiệu dữ liệu bị mất. Đã thêm parser nhỏ, chỉ chạy sau khi JSON đã từ chối input.
Kiểm thử (trên dữ liệu thật)
1.222/1.222 ảnh nội thất → có share URL /share/i/
1.222/1.222 ảnh nội thất → có nội dung panel
0 rác entity (… /) trong text hiển thị
500/500 sản phẩm → mang slug, share URL dùng slug
300/300 sản phẩm có specs → giờ hiện thông số (trước: 0)
294/300 sản phẩm có tính năng → hiện trong panel
Hạ tầng SEO (đã có sẵn, không cần sửa)
Đã xác minh trực tiếp trên vai-avatar:
/share/i/<id>— HTTP 200, có<title>,canonical,og:title/description/image,keywords, JSON-LD, vàog:image:width/heightlấy theo tỉ lệ ảnh thật (1200×1396 cho ảnh dọc) thay vì 1024×768 cố định/share/p/<slug>— HTTP 200 với slug sản phẩm thật- sitemap + robots.txt
Lưu ý
PR này chỉ chứa source. dist/assets/ là build output — Space phục vụ ./dist/assets/index.js, nên vẫn cần npm run build để thấy thay đổi. Sandbox của tôi đã mất kết nối và hf_jobs bị từ chối (402, hết credit ở cả bep40 lẫn ml-intern-explorers), nên tôi chưa build được. Build chỉ là npm ci && npm run build rồi commit dist/assets/ — emptyOutDir: false nên 6.372 ảnh prodimgs và các file JSON không bị ảnh hưởng.
Rebuilt and verified in a browser, which the previous version of this PR had not been.
The fix that mattered. ImageField creates one DOM node per photograph and reuses it across searches. The node's onclick closed over the record it was created from — and on first paint that record is the ambient entry from collection.json, which holds only id/url/ar/blur. layout() refreshed node.target on every pass but never node.image. So any photograph already on screen before you searched (180 of the 1,222) kept opening with no title, no room, no categories and no source link, however well it matched. Layout now writes node.image and the click handler reads it at click time.
What the viewer shows now. Restored src/detail.js from ffe2656 — the commit that carried it, reverted along with the wiring — and renders its derivation as a collapsible panel: heading, description, room and topic tags, a fact table, and a link back to the source article. It is the same derivation vai-avatar's /share/ page uses, so the panel and a shared link agree on what a photograph is. The panel is suppressed when a record carries none of its own metadata, rather than offering a generic heading and a row of digits.
Share URLs address a product by its slug rather than its sku. MH800TC says nothing about what is linked; mh800tc-malloca-may-hut-mui-gan-tu-am-tu-80cm reads as the product's name to a person and to a crawler. Checked against the old module across 10 record shapes: exactly 2 differ, both intentional.
Measured in Chromium against the built bundle. After "phòng khách hiện đại": 72 interior results and 24 ambient tiles compose the field. A matched photograph opens showing its title (Sofa đã bàn giao cho căn hộ chung cư), its SOFA tag, its id and a working link to noithatphuphat.com. The panel is collapsed by default, opens on click and on Enter, and fits a 390px viewport without overflow. Ambient tiles show no panel. No uncaught JS errors.
Two corrections to what was recorded earlier. The claim that 8,053 rows store specs as a Python repr is wrong — scanning both source files finds zero, in products_with_slugs.json and in products-gallery.json alike. They are plain objects or literally {}. The parsePyRepr fallback written for it was dead code and is not in this commit. The real figure is 11,701 of 11,704 rows rendering spec facts.
Not verified in a browser, and why. Products. PRODUCT_EMBEDDINGS has never been generated, so product search has nothing to rank against and no product enters the field. Their panel path is covered by unit tests over the real catalogue instead.
main is untouched at 6b17593783a6; this is 29ed8a3.