Skip to content

🎞️ fix: Stack Slide Previews and Show File Cards on Every Message - #16496

Open
TomasPalsson wants to merge 25 commits into
LibreChat-AI:devfrom
TomasPalsson:fix/pptx-slide-scroll-file-cards
Open

TomasPalsson wants to merge 25 commits into
LibreChat-AI:devfrom
TomasPalsson:fix/pptx-slide-scroll-file-cards

Conversation

@TomasPalsson

@TomasPalsson TomasPalsson commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

When an agent's code run makes a PowerPoint deck, the side panel squeezes the whole deck into one box scaled as if it were a single slide: only slide 1 shows and the panel can't scroll to the rest. wrapSlides() in the pptx preview bootstrap walked container.children, but pptx-preview@1.0.7 nests every .pptx-preview-slide-wrapper inside one .pptx-preview-wrapper, so the whole deck was wrapped and scaled as one "slide". init also got a fixed height, which turned that box into its own scroll region.

Separately, when an agent remakes a file under the same name in a later turn (same file_id), its card showed on only one message in the whole chat, because card dedup claimed toolArtifactClaim(fileId) globally and the last card to mount won. A file written by two code runs in one message could also show twice.

Changes

  • packages/api/src/files/documents/html.ts: pptxPreview.init gets a width only. Each .pptx-preview-slide-wrapper is wrapped and scaled on its own, inserted via slide.parentNode, and its inline margin: 0 auto is cleared so panels wider than 992px don't shift and clip it. The library's wrapper box follows the content width on a transparent background, slides get 16px spacing, finalize() can't run twice, and html { scrollbar-gutter: stable } stops the refit flicker with space-taking scrollbars. The fallbacks (library missing, throws, empty render, 8s timeout) are unchanged.
  • Parts/claim.ts (new): useToolArtifactClaim scopes the card claim to ${messageId}::${id} (the bare id when there's no message), shared by ToolArtifactCard and ToolMermaidArtifact.
  • Parts/ToolArtifactCard.tsx: registers panel content only when its version is newer by lastUpdateTime; the old global latest-mount claim is kept only as the equal-time tie-break, so every card opens the newest version.
  • Parts/ToolMermaidArtifact.tsx, Mermaid/Mermaid.tsx: diagram cards offer their version to a per-file Jotai atom and register the newest one when opened, while the inline diagram keeps showing its own message's source. registerArtifact never overwrites a strictly newer entry.
  • SearchContent.tsx: passes the row's messageId to every part, so search results place cards per message too.
  • utils/map.ts: mapAttachments keeps one copy per file per message (the newest write; only tool-call-linked entries compete). FileAttachmentGroup uses the same fileIdentity.
  • utils/artifacts.ts: toolArtifactKey falls back file_id β†’ filepath β†’ filename (was file_id β†’ filename β†’ filepath), so two different id-less download-fallback files with the same name no longer collapse into one card.

No server, schema or config changes beyond the preview HTML, and no new script sources or CSP entries. Previews stored before this keep their old HTML until the deck is remade.

Type of change

  • Bug fix

Testing

  • New packages/api/src/files/documents/layout.spec.ts runs the real bootstrap in JSDOM against a fake pptxPreview that mirrors the library's DOM and inline styles: one block per slide at 360/768/1480px, 30 slides with no inner scroll box, resize refit, 16:9 and 4:3 ratios, every fallback mode, spacing, wide-panel pinning, scrollbar gutter. npx jest src/files/documents β†’ 220 passing.
  • New Parts/__tests__/ToolArtifactCard.test.tsx and Parts/__tests__/FileAttachmentGroup.test.tsx, plus new cases in utils/__tests__/map.test.ts and utils/__tests__/artifacts.test.ts. npx jest src/components/Chat/Messages/Content src/components/Messages/Content/Mermaid src/components/Artifacts src/hooks/Artifacts src/hooks/Files src/utils/__tests__/map.test.ts src/utils/__tests__/artifacts.test.ts β†’ 1977 passing. The existing ArtifactRouting.test.tsx no-message cases are unchanged.
  • Real browser: python-pptx decks rendered through this branch's pptxToHtml with the pinned pptx-preview@1.0.7, at 360–1480px and live resize: one block per slide, width = panel βˆ’ 32px, 0px sideways overflow, last slide reachable.
  • Running app (this branch's build with the e2e fake model; chats seeded in the DB because the fake code runner only makes CSV): a remade report.pptx shows a card on both turns and the turn-1 card opens the newest deck; a remade .mmd opens its newest version from the older message; a file written by two runs in one message shows once; the panel scrolls to the last slide at narrow and expanded widths.
  • Not verified: the scrollbar-gutter fix on native classic scrollbars (Windows/Linux), since macOS only gives overlay scrollbars.
  • npx tsc --noEmit clean on packages/api and client; npm run static-checks clean on the changed files.

Test Configuration

  • Chrome / macOS.
  • Decks generated with python-pptx (12-slide 16:9, 6-slide 4:3, 8-slide remake).

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have added tests that prove my fix works
  • Local unit tests pass with my changes

…version

Two cards for the same file across different messages now both render
instead of one message winning the only visible chip. Registration into
the artifact panel now compares update timestamps (falling back to a
mount-order tie-break) so an older card mounting after a newer one, or
remounting after the newer card unmounts, can never clobber the newer
content. Search and shared conversation views now scope every rendered
part to its own message so the same fix applies there.
Extends the JSDOM pptx bootstrap harness with an options object (native
aspect ratio, renderer install/behavior, a capturable 8s safety-net
timer, and a mutable render-slot width) so it can drive every panel
width, a 30-slide deck with no inner scroll box, a resize-triggered
refit, both 16:9 and 4:3 decks, and each fallback trigger (renderer
missing, renderer throws, empty slide list, empty slide wrappers,
render timeout).

Clears the pptx-preview library's own inline width/background once
slides are wrapped, since JSDOM's getComputedStyle doesn't apply the
existing stylesheet's !important override the way a real browser does,
and restores the 16px spacing between stacked slide blocks now that
they live inside the library's own wrapper box instead of directly
under #lc-render.
…sage

A message that ran a tool twice on the same output file (e.g. rewriting
data.zip) showed a card for every run. mapAttachments now collapses
attachments that share a file identity to their last occurrence before
grouping by tool call, so a repeated file surfaces once, under its
newest run. Non-file attachments (no file_id or filepath) are untouched.
A code-execution diagram rewritten in a later turn now shows a card on
every message holding that file, but opening an older message's card
still displayed the stale content. Each ToolMermaidArtifact now offers
its version to a shared per-file "newest seen" record on mount, and
hands the newest entry to Mermaid for registration while keeping its
own inline render unchanged. Corrects two comments claiming shared
conversation views mount with no message context, which Share/Message
already provides.
… copy over a later unlinked duplicate, and key id-less files by filepath instead of filename
Copilot AI balanced review requested due to automatic review settings September 29, 2026 09:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

TomasPalsson and others added 5 commits September 29, 2026 09:51
@TomasPalsson
TomasPalsson marked this pull request as ready for review September 29, 2026 11:23
TomasPalsson added a commit to aproorg/LibreChat that referenced this pull request Sep 29, 2026
…breChat-AI#16496)

- Wrap and scale each pptx slide on its own at panel width, init pptx-preview with a width only, pin each slide to its block on wide panels, space the slides, guard finalize against running twice, and reserve the scrollbar gutter so the panel scrolls to the last slide without flicker.
- Scope file card dedup to the message and register panel content only when newer by lastUpdateTime (global latest-mount claim kept as the equal-time tie-break), so every message that made a file shows its card and every card opens the newest version; diagram cards and search results follow the same rule.
- Keep one copy per file per message (the newest write, tool-call-linked entries only), dedup the folded file group with the same identity, and key id-less files by path before name.
@TomasPalsson

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@danny-avila danny-avila added the πŸ—ΊοΈ Chat UI Shell codegraph: the taxonomy area this belongs to (classifier, confidence β‰₯ 0.9) label Sep 30, 2026
@codegraph-librechat codegraph-librechat Bot added the πŸ—ΊοΈ Chat Features codegraph: the taxonomy area this belongs to (classifier, confidence β‰₯ 0.9) label Oct 3, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

πŸ—ΊοΈ Chat Features codegraph: the taxonomy area this belongs to (classifier, confidence β‰₯ 0.9) πŸ—ΊοΈ Chat UI Shell codegraph: the taxonomy area this belongs to (classifier, confidence β‰₯ 0.9)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants