fix(web): AI Trading Copilot modal — quick action cards layout
The Quick Actions in the AI Trading Copilot floating modal were broken: - Always 2 columns regardless of viewport (cards overflowed at narrow widths). - Used <Button> from primitives which is inline-flex internally; the two inline-block <span> children for title and prompt overlapped. - Prompt was truncated client-side with .slice(0, 55)+'...' instead of proper CSS line-clamp, causing inconsistent display. - No min-width: 0 on grid/card/text so content could push the card past the modal width (460px). Refactored: - Replaced <Button> with native <button> (cleaner default styling base). - New CSS classes copilot-quick-action-card, -title, -prompt with explicit flex-direction: column so title and description stack reliably. - Responsive grid: single column below 480px, 2 columns above. The modal is 460px wide on desktop and shrinks to viewport-32 on smaller screens, so this prevents card overflow on narrow phones. - Title: single-line truncate with ellipsis, full text exposed via title attribute on the button (tooltip + a11y). - Prompt: -webkit-line-clamp: 2 (no JS slice). Wraps cleanly on long prompts. word-break + overflow-wrap as guards. - Card: min-height 64px so they align in a row without text-length jumping. Hover, active, and focus-visible states for keyboard a11y. - Body container gets bottom padding so the last card is reachable above the composer input. Container changes: - .copilot-body class added to messages scroll container; bottom padding ensures composer doesn't crowd content. - The modal shell (existing) already uses display:flex; flex-direction: column; overflow:hidden so header stays top, body scrolls, composer stays bottom — preserved unchanged. No breaking changes to chat behavior, sendMessage flow, or quick action prompts. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
1fa2bcde3b
commit
343ffb48af
@ -675,7 +675,7 @@ export const ChatControl = ({ profiles, botState, onApplyProfile }: ChatControlP
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Messages */}
|
{/* Messages */}
|
||||||
<div className="flex-1 overflow-y-auto px-4 py-4 space-y-4" style={{
|
<div className="copilot-body flex-1 overflow-y-auto px-4 py-4 space-y-4" style={{
|
||||||
background: 'var(--background)',
|
background: 'var(--background)',
|
||||||
scrollbarWidth: 'thin',
|
scrollbarWidth: 'thin',
|
||||||
scrollbarColor: 'var(--border) transparent',
|
scrollbarColor: 'var(--border) transparent',
|
||||||
@ -984,33 +984,20 @@ export const ChatControl = ({ profiles, botState, onApplyProfile }: ChatControlP
|
|||||||
|
|
||||||
{/* Suggested quick actions - shown when only welcome message exists */}
|
{/* Suggested quick actions - shown when only welcome message exists */}
|
||||||
{messages.length <= 1 && !isLoading && (
|
{messages.length <= 1 && !isLoading && (
|
||||||
<div className="mt-2">
|
<div className="copilot-quick-actions">
|
||||||
<p style={{ fontSize: '9px', color: 'var(--muted-foreground)', textTransform: 'uppercase', letterSpacing: '0.1em', fontWeight: 700, marginBottom: '10px', paddingLeft: '4px' }}>Quick Actions</p>
|
<p className="copilot-quick-actions-label">Quick Actions</p>
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="copilot-quick-actions-grid">
|
||||||
{quickActions.map((action, i) => (
|
{quickActions.map((action, i) => (
|
||||||
<Button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
key={i}
|
key={i}
|
||||||
onClick={() => sendMessage(action.prompt)}
|
onClick={() => sendMessage(action.prompt)}
|
||||||
variant="ghost"
|
className="copilot-quick-action-card"
|
||||||
className="text-left px-3.5 py-3 rounded-xl transition-all"
|
title={action.label}
|
||||||
style={{
|
|
||||||
background: 'var(--card)',
|
|
||||||
border: '1px solid var(--border)',
|
|
||||||
cursor: 'pointer',
|
|
||||||
}}
|
|
||||||
onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {
|
|
||||||
e.currentTarget.style.borderColor = 'var(--ring)';
|
|
||||||
e.currentTarget.style.background = 'var(--accent-soft)';
|
|
||||||
}}
|
|
||||||
onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {
|
|
||||||
e.currentTarget.style.borderColor = 'var(--border)';
|
|
||||||
e.currentTarget.style.background = 'var(--card)';
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<span style={{ fontSize: '13px', display: 'block', marginBottom: '3px', color: 'var(--foreground)' }}>{action.label}</span>
|
<span className="copilot-quick-action-title">{action.label}</span>
|
||||||
<span style={{ fontSize: '10px', color: 'var(--muted-foreground)', lineHeight: '1.4', display: 'block' }}>{action.prompt.slice(0, 55)}...</span>
|
<span className="copilot-quick-action-prompt">{action.prompt}</span>
|
||||||
</Button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -388,3 +388,108 @@
|
|||||||
|
|
||||||
/* In the bottom-nav mobile state (≤560px), the sidebar becomes a horizontal
|
/* In the bottom-nav mobile state (≤560px), the sidebar becomes a horizontal
|
||||||
* bar — the existing CSS hides the logo. Don't add anything that fights it. */
|
* bar — the existing CSS hides the logo. Don't add anything that fights it. */
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* 24. AI Trading Copilot modal — Quick Action cards layout
|
||||||
|
*
|
||||||
|
* The original quick actions used grid-cols-2 always, with two stacked
|
||||||
|
* <span style={display:block}> children inside a flex Button — which caused
|
||||||
|
* title/description overlap and overflow at narrow widths.
|
||||||
|
*
|
||||||
|
* New structure: native <button> with proper flex-column, responsive grid,
|
||||||
|
* title (truncate) + prompt (line-clamp-2). No overlap, no overflow.
|
||||||
|
* ============================================================================ */
|
||||||
|
.copilot-body {
|
||||||
|
/* Bottom padding ensures the last quick action / message is reachable
|
||||||
|
* above the composer input. */
|
||||||
|
padding-bottom: 16px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-quick-actions {
|
||||||
|
margin-top: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-quick-actions-label {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
margin: 0 0 10px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-quick-actions-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 480px) {
|
||||||
|
.copilot-quick-actions-grid {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-quick-action-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
text-align: left;
|
||||||
|
gap: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--card);
|
||||||
|
color: var(--foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.05s ease;
|
||||||
|
font-family: inherit;
|
||||||
|
/* Keep full card height predictable so they line up in the grid */
|
||||||
|
min-height: 64px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-quick-action-card:hover {
|
||||||
|
border-color: var(--ring);
|
||||||
|
background: var(--accent-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-quick-action-card:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-quick-action-card:focus-visible {
|
||||||
|
outline: 2px solid var(--ring);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-quick-action-title {
|
||||||
|
display: block;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--foreground);
|
||||||
|
line-height: 1.2;
|
||||||
|
/* Truncate to a single line — full text is in title attribute */
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copilot-quick-action-prompt {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
line-height: 1.4;
|
||||||
|
min-width: 0;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user