mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
fix(desktop): scope inline image gallery hover
Inline image galleries live inside chat message shells that already use Tailwind group hover for message-level affordances. Reusing the unqualified group selector on image tiles made a parent message hover reveal every fullscreen affordance in the gallery at once. Naming the image tile group keeps the overlay state local to the hovered tile while preserving the existing layout and click behavior. Constraint: Desktop chat message shells also use generic Tailwind group hover selectors. Rejected: Track hovered image index in React state | unnecessary runtime state for a selector-scoping issue Confidence: high Scope-risk: narrow Directive: Keep image-tile hover selectors scoped if this gallery remains nested inside message shells. Tested: cd desktop && bun run test InlineImageGallery.test.tsx --run Tested: bun run check:desktop Tested: git diff --check Not-tested: Manual desktop screenshot smoke
This commit is contained in:
parent
998e85efab
commit
4f0025700f
@ -64,6 +64,26 @@ describe('InlineImageGallery', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('scopes image hover overlays to each image tile', () => {
|
||||
render(
|
||||
<div className="group">
|
||||
<InlineImageGallery
|
||||
text={'abs /Users/me/pics/photo.png and rel outputs/b/chart.png'}
|
||||
sessionId="s1"
|
||||
workDir="/w"
|
||||
/>
|
||||
</div>,
|
||||
)
|
||||
|
||||
const firstTile = screen.getByRole('button', { name: /photo\.png/i })
|
||||
expect(firstTile).toHaveClass('group/image')
|
||||
expect(firstTile).not.toHaveClass('group')
|
||||
|
||||
const overlay = firstTile.querySelector('.group-hover\\/image\\:opacity-100')
|
||||
expect(overlay).not.toBeNull()
|
||||
expect(firstTile.querySelector('.group-hover\\:opacity-100')).toBeNull()
|
||||
})
|
||||
|
||||
it('does not render an in-workspace absolute path twice (dedup by basename)', () => {
|
||||
// The absolute path is INSIDE workDir, so extractAssistantOutputTargets also
|
||||
// surfaces it as a relative target (frame.png). It must only render once.
|
||||
|
||||
@ -114,7 +114,7 @@ export function InlineImageGallery({ text, sessionId, workDir }: Props) {
|
||||
key={img.src}
|
||||
type="button"
|
||||
onClick={() => setActiveIndex(i)}
|
||||
className="group relative overflow-hidden rounded-xl border border-[var(--color-border)] bg-[var(--color-surface-container-low)] text-left shadow-sm transition-all hover:shadow-md hover:border-[var(--color-brand)]/40"
|
||||
className="group/image relative overflow-hidden rounded-xl border border-[var(--color-border)] bg-[var(--color-surface-container-low)] text-left shadow-sm transition-all hover:shadow-md hover:border-[var(--color-brand)]/40"
|
||||
>
|
||||
<img
|
||||
src={img.src}
|
||||
@ -127,7 +127,7 @@ export function InlineImageGallery({ text, sessionId, workDir }: Props) {
|
||||
(e.target as HTMLImageElement).closest('button')!.style.display = 'none'
|
||||
}}
|
||||
/>
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-black/0 opacity-0 transition-all group-hover:bg-black/20 group-hover:opacity-100">
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-black/0 opacity-0 transition-all group-hover/image:bg-black/20 group-hover/image:opacity-100">
|
||||
<span className="material-symbols-outlined rounded-full bg-white/90 p-2 text-[20px] text-[var(--color-text-primary)] shadow-lg">
|
||||
fullscreen
|
||||
</span>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user