fix(client): compose shared buttons for two disclosures and seed re-enabled ask drafts

Compose the shared Button for the memory-artifacts and web-search disclosure
rows, keeping only their geometry local. The web-search row keeps its instant
hover by suppressing the recipe's color transition, since the chevron beside it
reveals without a fade and the existing test pins that.

Seed the ask-specific draft when Save drafts is re-enabled. An answer captured
while saving was off has no persisted ask draft for autosave to restore, so
expanding opened an empty composer and the next collapse read that empty value
back over the in-memory answer.
This commit is contained in:
Marco Beretta 2026-08-28 22:45:34 +02:00
parent bf7ae2549b
commit 3565f77171
No known key found for this signature in database
GPG key ID: D918033D8E74CC11
3 changed files with 22 additions and 12 deletions

View file

@ -1,7 +1,7 @@
import { useId, useMemo, useState } from 'react';
import { ChevronDown } from 'lucide-react';
import { Tools } from 'librechat-data-provider';
import { disclosureChevronVariants } from '@librechat/client';
import { Button, disclosureChevronVariants } from '@librechat/client';
import type { MemoryArtifact, TAttachment } from 'librechat-data-provider';
import { toolPanelSpacingClassName } from './disclosure';
import { useExpandCollapse, useLocalize } from '~/hooks';
@ -61,12 +61,12 @@ export default function MemoryArtifacts({ attachments }: { attachments?: TAttach
return (
<>
<div className="relative my-1 flex h-5 shrink-0 items-center gap-2.5">
<button
<Button
variant="ghost"
className={cn(
'group/disclosure inline-flex w-full items-center gap-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-heavy',
'group/disclosure h-auto w-full justify-start gap-2 rounded-none p-0 font-normal hover:bg-transparent',
hasErrors ? 'text-status-error' : 'text-text-secondary',
)}
type="button"
onClick={() => setShowInfo((prev) => !prev)}
aria-expanded={showInfo}
aria-controls={contentId}
@ -104,7 +104,7 @@ export default function MemoryArtifacts({ attachments }: { attachments?: TAttach
)}
aria-hidden="true"
/>
</button>
</Button>
</div>
<div
id={contentId}

View file

@ -282,13 +282,15 @@ export default function WebSearch({
{completedText}
</span>
<div className="relative flex h-5 items-center gap-1.5">
<button
type="button"
<Button
variant="ghost"
className={cn(
'tool-status-text group/disclosure flex h-5 min-w-0 items-center gap-2 rounded-full',
hasSourceData
? 'text-text-secondary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-heavy'
: 'pointer-events-none text-text-secondary',
'tool-status-text group/disclosure h-5 min-w-0 justify-start gap-2 rounded-full p-0 font-normal text-text-secondary hover:bg-transparent',
/** This row is a status line, not a padded control: the shared
* recipe's color transition would turn its hover into a fade,
* and the chevron reveal beside it is deliberately instant. */
'transition-none',
!hasSourceData && 'pointer-events-none disabled:opacity-100',
)}
disabled={!hasSourceData}
onClick={hasSourceData ? handleToggleSources : undefined}
@ -311,7 +313,7 @@ export default function WebSearch({
aria-hidden="true"
/>
)}
</button>
</Button>
{hasDetails && (
<HoverCard openDelay={50} open={showDetails} onOpenChange={setShowDetails}>
{/* Composed through `asChild` so the shared Button owns the

View file

@ -212,6 +212,14 @@ export default function useAskAnswerMode(conversationId?: string | null) {
* drafts are disabled, perform that handoff directly. */
if (!batchMode && !saveDrafts) {
formContext?.setValue('text', answerText);
} else if (!batchMode && answerText) {
/** Drafts were re-enabled after this answer was captured with saving
* off, so no ask draft exists for autosave to restore: the composer
* would open empty and the next collapse would read that empty
* value back over the in-memory answer. Seed it so the handoff has
* something to read. The in-memory value is authoritative because
* `setAnswerText` writes both while saving is on. */
setDraft({ id: getAskAnswerDraftId(liveAsk.actionId), value: answerText });
}
setCollapsedIds((prev) => prev.filter((id) => id !== liveAsk.actionId));
});