Spaces:
Running
Running
Commit History
Yesterday
-
add photo-date style
Verified signature290bf1c
9 days ago
-
refactor(css): drop the duplicated ringed-tile override; neon-frame.css owns it
Verified signature87f3556 -
refactor(css): import the neon frame from its own module so a rebuild keeps it
Verified signature703cbe5 -
Neon: run the light around the frame's perimeter instead of spinning the ring layer
Verified signatureThe ring was a full-size ::before carrying BOTH the conic gradient and the 2px border mask, and the animation rotated that whole layer. The mask rotates with it, so on a rectangular tile the painted border no longer matched the tile's own edges -- the highlight appeared clipped, skewed and partly off the frame. On a near-square tile it happened to look right, which is why it read as "a static neon border" rather than as a bug. Rotate the gradient's angle instead, and leave the layer still. @property is required so the angle is interpolable; the animation is gated behind @supports at-rule(@property) because an unregistered custom property would jump discretely at 50% and read as a strobe.83cbb13 -
Restore viewer CSS, ring only the newest ten, and clear the search pill
ML Intern1. CSS. The previous commit stripped the size/position controls with a script that cut from .field-controls to the end of the file, taking 147 lines with it: every .photo-* rule, .search-status, .search-progress and .footer. It went unnoticed because an unstyled <dialog> still opens, still shows the photograph, and never throws -- it just renders a raw UA box with the share toolbar stacked as plain text. Restored verbatim from the last good revision; app.css goes 17.1kB -> 24.7kB. 2. Ten rings, not sixty. RECENT_LIMIT (shown) and FEATURED_LIMIT (ringed) are now separate constants, because "show more photographs" and "only the newest glow" pull in opposite directions if one list drives both. 24 rings on a dense field is decoration rather than emphasis. The rest still render -- as ordinary tiles, which is what the brief asks for and what makes the ten legible. 3. More photographs: fieldCount 96/56 -> 150/96. Raised less on phones because tile area is bo…f134575 -
Ring every exact hit with running neon, and sync vai-avatar's newest photos
ML InternFour changes, all of them visible at a glance: 1. The neon outline now RUNS. It was a static ring with a breathing glow; it is a conic gradient masked to a 2px border and rotated every frame, so the light travels around the edge. An animated border-color cannot do this (borders take no gradient) and an animated box-shadow only pulses in place. The stroke is on ::before and the halo on ::after, because one pseudo-element can hold only one background and the ring needs both. mask-composite turns the full-size gradient into an outline; without it the gradient would fill the tile and hide the photograph, so the test asserts the mask is present. 2. Interiors published on vai-avatar now appear here. src/avatar-interiors.js reads bep40/vai-avatar2-interior/index.json, which is the file carrying created_at -- 153 rows, newest 2026-10-05. metadata.json has 1,752 rows but no timestamps at all, so it cannot answer "newest". These are matched by TEXT, not by th…eb92bc7 -
V.AI STUDIO backdrop, vai-avatar's product search, size/position, neon
ML InternFour changes, all on the search field. Background. The same artwork vai-avatar shows on its body (public/avatars/vaistudio_center.png, copied byte-for-byte, md5 checked) at 30% opacity. It is a separate fixed layer rather than a background-image on .app because opacity on .app would fade the photographs and the search pill with it, and background-attachment:fixed cannot compose with the .vignette and .grain layers already sitting on top of the field. Product search. Ranking is now vai-avatar's own scoreProduct(), ported to product-search.js rather than extended in place, so the two modules can be diffed against each other when the original changes. The whole rule set carries over: exact SKU/model/title must win outright, millimetre sizes are compared numerically (the specs write "900 mm", "900mm" and "W900" for the same shelf), material words are matched against specs as well as title, and brand names get a flat bonus. 35 tests in scripts/test-product-search.mjs pin each rule. Both s…0df020e
11 days ago
-
keep the share button's label on a phone screen
ML InternChanging the control from <a target="_blank"> to <button> to reach navigator.share kept its class name, and the class carried a rule written for the anchor: at 560px and below, .photo-share span { display: none }. On a phone that left a 44px button with no visible label and no accessible name, since display:none removes the text from the accessibility tree too -- and the label is where the "Đã chia sẻ" confirmation appears, so the phone could not see the share succeed. Clipped out of sight instead: the span keeps a 1px box and clip-path: inset(50%), which is the standard visually-hidden pattern. The text stays in the accessibility tree, so the button is still named, and the confirmation still updates -- it just is not drawn on a screen with no room for it. Measured on the built app at 1280px and on a Pixel 7 viewport: button is 44x44 in both, the span computes to position:absolute with clip-path:inset(50%), and a click still reaches navigator.share and rewrites the label to "Đã chia …1d2e122 -
Share link on every photograph, a real description, no source button
ML InternThree things the viewer was missing, all reported from using it. A share link on each photograph in the field. Every tile now carries one, pointing at the same public address the viewer's toolbar link does: /share/i/<id> for an interior photograph, /share/p/<slug> for a catalogue product. All 1,222 interiors and all 11,704 products resolve to one; the 180 ambient tiles in collection.json resolve too, because they carry ids. Hidden until hover, except the top three results, which show theirs outright -- 150 photographs each wearing a button turns the plane into a control panel. The button is aria-hidden and tabIndex -1, nested inside the tile's <button>. Nesting an <a> in a <button> is not valid HTML, so it is a pointer affordance only: the reachable share is the named link in the viewer. Clicking it opens a new tab -- navigating away would destroy the search, and the field has no route to come back to. Getting it clickable took three fixes, each measured rather than guessed: The sh…4641f85 -
viewer: name, describe and link every photo the search finds
ML InternOpening a photograph in the viewer showed nothing but the picture: no title, no room, no categories, no link back to the article it came from. The record carried all four -- worker.js spreads the whole image-embeddings.json row into each search hit, so title, categories and source arrive with the photograph. But nothing downstream ever kept them. ImageField creates one DOM node per photograph and reuses it across searches; the node's onclick closed over the record it was built from, and on first paint that record is the ambient entry from collection.json, which holds only id/url/ar/blur. A node's position was refreshed on every layout pass, its record was not. So any photograph that had been on screen before you searched -- 180 of the 1,222 -- kept opening with none of its metadata, no matter that it matched and now carried a title and a source. Layout now writes node.image alongside node.target, and the click handler reads it at click time. Adds src/detail.js, restored from ffe2656 …29ed8a3
12 days ago
-
feat: product share links + repair Malloca/Hafele misbranding
Verified signatureTwo things. Share links. vai-avatar served share pages for interior images and chat answers but nothing for products, so a product link had nowhere to point. A /share/p/<sku> route now exists there and resolves against VAIX_PRODUCTS, the 11,704-row catalogue that Space already loads. A "Xem thêm" button in the viewer toolbar links products to it and interiors to the existing /share/i/. The link is keyed on the photo, not the image, so paging a gallery still shares the product rather than one of its photographs. Misbranding. 99 rows are named "Malloca | ..." while their brand field says Hafele, and all 1,011 of their image URLs point at Hafele's CDN rather than the Malloca storefront. Hafele resells Malloca, so these are Malloca products catalogued under the wrong brand. 77 now show a real Malloca photograph and the remaining 22, which have no Malloca row anywhere in the file, keep their photo with the brand corrected. No product is dropped and no non-Malloca row is touched. Matching…9bf56b3 -
feat(field): merge Malloca products into the interior image field
Verified signaturee34a847
14 days ago
-
Upload src/index.css
Verified signaturefa651e5 -
Upload src/index.css
Verified signature8f2852f -
Upload src/index.css
Verified signaturef4e1e2e
15 days ago
-
Deploy Vietnamese interior image field (1222 imgs, multilingual CLIP text tower)
Verified signaturec9e39f3