Files
fusion/packages/dashboard/app/components/QuickChatFAB.css
gsxdsm 13ed470f9f fix(dashboard): mobile quick-chat panel layout under iOS keyboard
The mobile QuickChat panel was unusable on iOS Safari: the input bar
rendered off-screen, the soft keyboard often did not open on FAB tap,
the panel slid up out of view on the second input focus, and the
header pill overflowed when the model name was long.

- The FAB JSX no longer emits inline right/bottom styles on mobile,
  and the mobile media query pins every edge with !important so the
  panel cannot be dragged off-screen by the desktop draggable hook.
- Body scroll is locked while the panel is open via overflow:hidden
  on <html> and <body>, and the document is reset to scroll 0 on
  open. This prevents iOS from leaking residual scroll into the
  fixed-positioned panel between opens.
- An always-mounted, offscreen stealth input claims the iOS soft
  keyboard inside the FAB click gesture (the real composer input is
  initially `disabled` waiting for the chat session, and iOS will
  not open the keyboard for a disabled input). Focus is transferred
  synchronously to the real input once it becomes enabled.
- The composer input intercepts touchstart on mobile and re-focuses
  with `preventScroll: true`, suppressing iOS's default focus-and-
  scroll behavior that was shifting visualViewport.offsetTop and
  sliding the panel up off-screen on a refocus.
- A useLayoutEffect mirrors visualViewport.height onto the panel as
  --vv-height directly via the DOM, bypassing React state to avoid
  the per-event reconciliation lag that read as visual jank during
  the keyboard slide-in. On blur, the variable is cleared and a
  short suppress window (~450ms) prevents iOS's mid-dismiss reports
  from clobbering the regrowth, so the panel snaps back to full
  height immediately while the keyboard finishes sliding down on
  top of it.
- The model-tag pill in the header collapses to the provider icon
  when the tag would otherwise exceed ~12 characters on mobile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 21:27:10 -07:00

1098 lines
26 KiB
CSS

/* ── Quick Chat FAB ──────────────────────────────────────────────── */
/* Position set via inline style from useDraggable */
.quick-chat-fab {
position: fixed;
width: 48px;
height: 48px;
border-radius: 50%;
border: 1px solid color-mix(in srgb, var(--todo) 45%, var(--border));
background: var(--todo);
color: var(--cta-text);
display: inline-flex;
align-items: center;
justify-content: center;
box-shadow: 0 8px 20px color-mix(in srgb, var(--todo) 35%, var(--bg));
cursor: grab;
z-index: 1000;
transition: transform var(--transition-fast), box-shadow var(--transition-fast), filter var(--transition-fast);
}
.quick-chat-fab:hover {
transform: translateY(-2px);
box-shadow: 0 12px 24px color-mix(in srgb, var(--todo) 40%, var(--bg));
filter: brightness(1.04);
}
.quick-chat-fab:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
}
/* Disable hover effects during drag for responsiveness */
.quick-chat-fab[data-dragging="true"] {
cursor: grabbing;
transform: scale(1.1);
transition: none;
}
.quick-chat-fab--hidden {
opacity: 0;
pointer-events: none;
visibility: hidden;
}
/* Always-mounted invisible input. Focused inside the FAB click gesture
to claim the iOS soft keyboard before the real composer input renders.
Must be focusable (no `display: none`, no `visibility: hidden`) and big
enough that iOS treats focus as a real intent — so we tuck it offscreen
instead of hiding it. `font-size: 16px` defeats the iOS focus-zoom. */
.quick-chat-stealth-input {
position: fixed;
bottom: 0;
left: 0;
width: 1px;
height: 1px;
padding: 0;
margin: 0;
border: 0;
opacity: 0;
pointer-events: none;
font-size: 16px;
z-index: -1;
}
/* Position set via inline style from useDraggable */
.quick-chat-panel {
--quick-chat-min-width: 280px;
--quick-chat-min-height: 260px;
position: fixed;
width: 320px;
height: 400px;
min-width: var(--quick-chat-min-width);
min-height: var(--quick-chat-min-height);
display: flex;
flex-direction: column;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
overflow: hidden;
z-index: 1001;
}
.quick-chat-resize-handle {
position: absolute;
z-index: 2;
background: transparent;
/* Faint accent line appears on hover to indicate resizability */
transition: background var(--transition-fast);
}
/* Edge handles — inset matches corner handle size so they don't overlap. */
.quick-chat-resize-handle[data-resize-direction="n"] {
cursor: n-resize;
top: 0;
left: 16px;
right: 16px;
height: 6px;
}
.quick-chat-resize-handle[data-resize-direction="s"] {
cursor: s-resize;
bottom: 0;
left: 16px;
right: 16px;
height: 6px;
}
.quick-chat-resize-handle[data-resize-direction="e"] {
cursor: e-resize;
top: 16px;
right: 0;
bottom: 16px;
width: 6px;
}
.quick-chat-resize-handle[data-resize-direction="w"] {
cursor: w-resize;
top: 16px;
left: 0;
bottom: 16px;
width: 6px;
}
/* Corner handles — sized to clear the panel's 12px border-radius so the
diagonal hit target reaches outside the curve. */
.quick-chat-resize-handle[data-resize-direction="nw"] {
cursor: nw-resize;
top: 0;
left: 0;
width: 16px;
height: 16px;
}
.quick-chat-resize-handle[data-resize-direction="ne"] {
cursor: ne-resize;
top: 0;
right: 0;
width: 16px;
height: 16px;
}
.quick-chat-resize-handle[data-resize-direction="sw"] {
cursor: sw-resize;
bottom: 0;
left: 0;
width: 16px;
height: 16px;
}
.quick-chat-resize-handle[data-resize-direction="se"] {
cursor: se-resize;
bottom: 0;
right: 0;
width: 16px;
height: 16px;
}
/* Subtle hover accent on edge handles */
.quick-chat-resize-handle[data-resize-direction="n"]:hover,
.quick-chat-resize-handle[data-resize-direction="s"]:hover {
background: linear-gradient(
to bottom,
transparent 30%,
color-mix(in srgb, var(--border) 60%, transparent) 50%,
transparent 70%
);
}
.quick-chat-resize-handle[data-resize-direction="e"]:hover,
.quick-chat-resize-handle[data-resize-direction="w"]:hover {
background: linear-gradient(
to right,
transparent 30%,
color-mix(in srgb, var(--border) 60%, transparent) 50%,
transparent 70%
);
}
/* Subtle hover accent on corner handles — small radial dot that hints at
the diagonal grip without competing with the panel's rounded border. */
.quick-chat-resize-handle[data-resize-direction="nw"]:hover,
.quick-chat-resize-handle[data-resize-direction="ne"]:hover,
.quick-chat-resize-handle[data-resize-direction="sw"]:hover,
.quick-chat-resize-handle[data-resize-direction="se"]:hover {
background: radial-gradient(
circle at center,
color-mix(in srgb, var(--border) 70%, transparent) 0%,
color-mix(in srgb, var(--border) 30%, transparent) 50%,
transparent 75%
);
}
.quick-chat-panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
border-bottom: 1px solid var(--border);
background: color-mix(in srgb, var(--surface) 88%, var(--card));
}
.quick-chat-panel-header h3 {
margin: 0;
font-size: 0.95rem;
font-weight: 600;
color: var(--text);
}
.quick-chat-panel-title-wrap {
display: flex;
align-items: center;
gap: var(--space-sm);
min-width: 0;
}
.quick-chat-model-tag {
display: inline-flex;
align-items: center;
max-width: 18ch;
padding: var(--space-xs) var(--space-sm);
border-radius: var(--radius-pill);
border: 1px solid color-mix(in srgb, var(--todo) 35%, var(--border));
background: color-mix(in srgb, var(--todo) 14%, transparent);
color: var(--text);
font-size: 0.75rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
@media (min-width: 769px) {
.quick-chat-model-tag {
max-width: 14ch;
font-size: 0.6875rem;
line-height: 1.2;
white-space: normal;
overflow-wrap: anywhere;
text-overflow: clip;
}
}
/* Icon-only fallback when the model name is too long for the header. */
.quick-chat-model-tag--icon {
max-width: none;
padding: var(--space-xs);
justify-content: center;
}
.quick-chat-panel-header-actions {
--quick-chat-header-control-size: calc(var(--space-lg) + var(--space-sm) + var(--space-xs));
display: inline-flex;
align-items: center;
gap: var(--space-xs);
}
.quick-chat-panel-header-actions .btn-icon {
width: var(--quick-chat-header-control-size);
min-width: var(--quick-chat-header-control-size);
height: var(--quick-chat-header-control-size);
min-height: var(--quick-chat-header-control-size);
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid var(--border);
border-radius: var(--radius-md);
background: var(--card);
color: var(--text);
}
.quick-chat-header-mode-toggle {
display: inline-flex;
align-items: center;
gap: calc(var(--space-xs) / 2);
}
.quick-chat-mode-btn {
min-width: var(--quick-chat-header-control-size);
height: var(--quick-chat-header-control-size);
min-height: var(--quick-chat-header-control-size);
padding: 0 var(--space-sm);
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid var(--border);
border-radius: var(--radius-pill);
background: transparent;
color: var(--text-muted);
font-size: 12px;
font-weight: 600;
line-height: 1;
cursor: pointer;
transition: var(--transition-fast);
white-space: nowrap;
}
.quick-chat-panel-header-actions .btn-icon.quick-chat-new-chat-btn {
background: var(--cta-bg);
border-color: var(--cta-border);
color: var(--cta-text);
}
.quick-chat-panel-header-actions .btn-icon.quick-chat-new-chat-btn:hover {
background: var(--cta-bg-hover);
border-color: var(--cta-border-hover);
color: var(--cta-text);
}
.quick-chat-panel-header-actions .btn-icon.quick-chat-new-chat-btn:disabled,
.quick-chat-panel-header-actions .btn-icon.quick-chat-new-chat-btn:disabled:hover {
background: var(--cta-bg);
border-color: var(--cta-border);
color: var(--cta-text);
opacity: 0.6;
cursor: not-allowed;
}
.quick-chat-mode-btn:hover {
background: color-mix(in srgb, var(--todo) 10%, transparent);
color: var(--text);
border-color: color-mix(in srgb, var(--todo) 40%, var(--border));
}
.quick-chat-mode-btn--active {
background: color-mix(in srgb, var(--todo) 18%, transparent);
color: var(--text);
border-color: color-mix(in srgb, var(--todo) 50%, var(--border));
font-weight: 500;
}
.quick-chat-mode-btn:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
}
.quick-chat-panel-agent-select {
padding: 10px 12px;
border-bottom: 1px solid var(--border);
}
.quick-chat-panel-agent-select select {
width: 100%;
background: var(--bg);
color: var(--text);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 7px 9px;
}
.quick-chat-panel-messages {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
gap: 8px;
overflow-y: auto;
padding: 10px 12px;
background: color-mix(in srgb, var(--bg) 35%, transparent);
}
.quick-chat-panel-empty {
margin: auto;
text-align: center;
color: var(--text-muted);
font-size: 0.85rem;
}
.quick-chat-panel-message {
max-width: 86%;
padding: 8px 10px;
border-radius: var(--radius-md);
border: 1px solid var(--border);
color: var(--text);
font-size: 0.85rem;
line-height: 1.45;
word-break: break-word;
}
.quick-chat-panel-message p {
margin: 0;
white-space: pre-wrap;
}
.quick-chat-message-content--plain {
white-space: pre-wrap;
}
.quick-chat-message-content--markdown {
white-space: normal;
overflow-wrap: anywhere;
}
.quick-chat-message-content--markdown > :first-child {
margin-top: 0;
}
.quick-chat-message-content--markdown > :last-child {
margin-bottom: 0;
}
.quick-chat-message-content--markdown p,
.quick-chat-message-content--markdown ul,
.quick-chat-message-content--markdown ol,
.quick-chat-message-content--markdown blockquote,
.quick-chat-message-content--markdown pre,
.quick-chat-message-content--markdown table {
margin: 0 0 var(--space-sm);
}
.quick-chat-message-content--markdown ul,
.quick-chat-message-content--markdown ol {
padding-left: var(--space-lg);
}
.quick-chat-message-content--markdown code {
font-family: var(--font-mono);
font-size: 0.75rem;
}
.quick-chat-message-content--markdown :not(pre) > code {
padding: 0 var(--space-xs);
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--surface) 55%, transparent);
}
.quick-chat-markdown-pre {
margin: 0 0 var(--space-sm);
padding: var(--space-sm);
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--surface) 65%, transparent);
overflow-x: auto;
white-space: pre;
max-width: 100%;
}
.quick-chat-markdown-table {
display: block;
width: 100%;
max-width: 100%;
overflow-x: auto;
border-collapse: collapse;
}
.quick-chat-markdown-table th,
.quick-chat-markdown-table td {
border: 1px solid color-mix(in srgb, var(--border) 85%, transparent);
padding: var(--space-xs) var(--space-sm);
}
.quick-chat-panel-message--sent {
align-self: flex-end;
background: color-mix(in srgb, var(--todo) 20%, transparent);
border-color: color-mix(in srgb, var(--todo) 45%, var(--border));
}
.quick-chat-panel-message--received {
position: relative;
align-self: flex-start;
background: var(--card);
}
.quick-chat-message-render-toggle {
position: absolute;
top: var(--space-xs);
right: var(--space-xs);
width: 20px;
height: 20px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity var(--transition-fast);
color: var(--text-muted);
background: transparent;
border: none;
border-radius: var(--radius-sm);
cursor: pointer;
outline: none;
}
.quick-chat-panel-message--received:hover .quick-chat-message-render-toggle,
.quick-chat-message-render-toggle:focus-visible {
opacity: 1;
}
.quick-chat-message-render-toggle--plain {
opacity: 1;
color: var(--accent);
}
.quick-chat-message-render-toggle:hover {
background: var(--surface-hover, color-mix(in srgb, var(--surface) 55%, transparent));
color: var(--text);
}
.quick-chat-message-render-toggle:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
}
.quick-chat-panel-input {
display: flex;
align-items: center;
gap: var(--space-sm);
padding: calc(var(--space-sm) + var(--space-xs)) var(--space-md);
border-top: 1px solid var(--border);
background: color-mix(in srgb, var(--surface) 85%, var(--bg));
}
.quick-chat-input-wrapper {
position: relative;
display: flex;
flex: 1;
flex-direction: column;
min-width: 0;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg);
padding: var(--space-xs);
}
.quick-chat-input-row {
display: flex;
align-items: center;
gap: var(--space-sm);
min-width: 0;
}
.quick-chat-input-wrapper--dragover {
border-color: var(--todo);
background: color-mix(in srgb, var(--todo) 5%, var(--bg));
}
.quick-chat-attachment-input {
display: none;
}
.quick-chat-panel-input input {
flex: 1;
min-width: 0;
border-radius: var(--radius-sm);
border: none;
background: transparent;
color: var(--text);
padding: var(--space-sm);
}
/* Keep the attachment trigger compact inside the input wrapper so the composer
stays a single inline row with the text field. */
.quick-chat-attach-btn {
min-width: calc(var(--space-lg) + var(--space-md));
min-height: calc(var(--space-lg) + var(--space-md));
width: calc(var(--space-lg) + var(--space-md));
height: calc(var(--space-lg) + var(--space-md));
flex-shrink: 0;
}
/* The send/stop action stays in the same inline composer row as attach + input. */
.quick-chat-send-btn {
min-width: calc(var(--space-lg) + var(--space-xl));
min-height: calc(var(--space-lg) + var(--space-xl));
width: calc(var(--space-lg) + var(--space-xl));
height: calc(var(--space-lg) + var(--space-xl));
border-radius: var(--radius-sm);
border: 1px solid color-mix(in srgb, var(--todo) 45%, var(--border));
background: var(--todo);
color: var(--cta-text);
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.quick-chat-send-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.quick-chat-attachment-previews {
display: flex;
gap: var(--space-xs);
padding: var(--space-sm) var(--space-md);
border-top: 1px solid var(--border);
overflow-x: auto;
background: color-mix(in srgb, var(--surface) 85%, var(--bg));
}
.quick-chat-attachment-preview {
width: calc(var(--space-lg) * 3);
height: calc(var(--space-lg) * 3);
border-radius: var(--radius-sm);
border: 1px solid var(--border);
position: relative;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
background: color-mix(in srgb, var(--surface) 65%, var(--bg));
overflow: hidden;
}
.quick-chat-attachment-preview img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: inherit;
}
.quick-chat-attachment-preview-name {
width: 100%;
padding: var(--space-xs);
font-size: calc(var(--space-sm) + var(--space-xs));
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--text-muted);
}
.quick-chat-attachment-remove {
position: absolute;
top: 0;
right: 0;
transform: translate(var(--space-xs), calc(var(--space-xs) * -1));
background: color-mix(in srgb, var(--color-error) 80%, transparent);
color: var(--cta-text);
border: none;
border-radius: 50%;
width: calc(var(--space-md) + var(--space-md));
height: calc(var(--space-md) + var(--space-md));
font-size: var(--space-sm);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
line-height: 1;
}
.quick-chat-attachment-remove:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
}
/* === Quick Chat Mobile (FN: full-screen) =================================== */
@media (max-width: 768px) {
.quick-chat-panel {
/* Full-screen sheet that ignores drag position on mobile. We pin
every edge with !important so any stray inline value cannot pull
the panel off-screen. iOS's would-be visual-viewport shift on
input focus is suppressed at the source by the input's
onTouchStart handler in QuickChatFAB.tsx, so we don't need to
compensate with translateY here. */
position: fixed !important;
inset: 0 !important;
left: 0 !important;
right: 0 !important;
top: 0 !important;
bottom: 0 !important;
width: 100vw !important;
height: 100vh !important;
height: 100dvh !important;
height: var(--vv-height, 100dvh) !important;
max-width: 100vw;
max-height: 100vh;
max-height: 100dvh;
max-height: var(--vv-height, 100dvh);
border-radius: 0;
border: none;
z-index: 1100;
}
.quick-chat-resize-handle {
display: none;
}
.quick-chat-panel-messages {
/* Let messages take all remaining space between header and input */
flex: 1 1 auto;
min-height: 0;
}
.quick-chat-panel-input {
/* Reserve safe-area space so the input clears the iOS home bar */
padding-bottom: calc(var(--space-sm) + var(--space-xs) + env(safe-area-inset-bottom, 0px));
}
.quick-chat-attachment-previews {
padding: var(--space-sm) max(var(--space-md), env(safe-area-inset-left, 0px));
}
.quick-chat-attachment-preview {
width: calc(var(--space-lg) * 2 + var(--space-sm));
height: calc(var(--space-lg) * 2 + var(--space-sm));
}
.quick-chat-send-btn {
min-width: calc(var(--space-lg) + var(--space-xl));
min-height: calc(var(--space-lg) + var(--space-xl));
}
.quick-chat-attachment-remove {
min-width: calc(var(--space-lg) + var(--space-xl));
min-height: calc(var(--space-lg) + var(--space-xl));
width: calc(var(--space-lg) + var(--space-xl));
height: calc(var(--space-lg) + var(--space-xl));
top: 0;
right: 0;
transform: translate(var(--space-xs), calc(var(--space-xs) * -1));
font-size: var(--space-md);
}
.quick-chat-panel-header {
flex-wrap: nowrap;
row-gap: 0;
/* iOS PWA: status bar (black-translucent) overlays the panel from top: 0,
clipping the close button. Reserve top inset on mobile. */
padding-top: calc(10px + env(safe-area-inset-top, 0px));
}
.quick-chat-panel-title-wrap {
flex: 1 1 auto;
min-width: 0;
}
.quick-chat-panel-header h3 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.quick-chat-model-tag {
max-width: 12ch;
flex-shrink: 1;
}
.quick-chat-panel-header-actions {
--quick-chat-header-control-size: calc(var(--space-xl) + var(--space-md));
width: auto;
flex-shrink: 0;
justify-content: flex-end;
min-width: 0;
}
.quick-chat-panel .chat-tool-calls-group-summary {
flex-wrap: nowrap;
flex-direction: row;
align-items: center;
}
.quick-chat-panel .chat-tool-calls-names,
.quick-chat-panel .chat-tool-call-name,
.quick-chat-panel .chat-tool-call-status-text,
.quick-chat-panel .chat-tool-calls-group-status {
white-space: nowrap;
}
.quick-chat-panel .chat-tool-calls-group-status {
justify-content: flex-end;
margin-left: auto;
}
}
.chat-message-thinking-content {
font-size: 11px;
color: var(--text-tertiary);
margin-top: 4px;
}
.chat-message-thinking {
margin-top: 6px;
}
.chat-message-thinking summary {
font-size: 12px;
color: var(--text-secondary);
cursor: pointer;
}
.chat-message-thinking-content {
font-size: 13px;
color: var(--text-secondary);
padding: 8px;
background: var(--bg);
border-radius: 6px;
margin-top: 4px;
white-space: pre-wrap;
font-family: var(--font-mono, monospace);
overflow-x: auto;
}
/* === Chat Tool Calls === */
.chat-tool-calls {
margin-top: var(--space-sm);
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.chat-tool-calls-header {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
color: var(--text-muted);
font-size: var(--space-md);
}
.chat-tool-calls-group {
border: var(--btn-border-width, 1px) solid color-mix(in srgb, var(--border) 85%, transparent);
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--surface) 35%, transparent);
}
.chat-tool-calls-group-summary {
display: flex;
align-items: center;
gap: var(--space-sm);
list-style: none;
cursor: pointer;
padding: var(--space-xs) var(--space-sm);
color: var(--text-muted);
border-radius: var(--radius-sm);
font-size: var(--space-md);
min-width: 0;
white-space: nowrap;
}
.chat-tool-calls-group-summary::marker {
content: "";
}
.chat-tool-calls-group-summary::-webkit-details-marker {
display: none;
}
.chat-tool-calls-group-summary:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
}
.chat-tool-calls-count {
white-space: nowrap;
flex-shrink: 0;
}
.chat-tool-calls-names {
color: var(--text-dim);
font-family: var(--font-mono, monospace);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1 1 auto;
min-width: 0;
font-size: 0.6875rem;
}
.chat-tool-calls-group-status {
margin-left: auto;
font-size: 0.6875rem;
color: var(--text-dim);
flex-shrink: 0;
white-space: nowrap;
}
.chat-tool-calls-group > .chat-tool-call {
margin: 0 var(--space-sm) var(--space-xs) var(--space-sm);
}
.chat-tool-calls-group > .chat-tool-call:last-child {
margin-bottom: var(--space-sm);
}
.chat-tool-call {
border: var(--btn-border-width, 1px) solid color-mix(in srgb, var(--border) 85%, transparent);
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--surface) 35%, transparent);
}
.chat-tool-call summary {
display: flex;
align-items: center;
gap: var(--space-xs);
list-style: none;
cursor: pointer;
padding: var(--space-xs) var(--space-sm);
color: var(--text-muted);
border-radius: var(--radius-sm);
font-size: var(--space-md);
min-width: 0;
white-space: nowrap;
}
.chat-tool-call summary::marker {
content: "";
}
.chat-tool-call summary::-webkit-details-marker {
display: none;
}
.chat-tool-call summary:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
}
.chat-tool-call-status-dot {
width: calc((var(--space-xs) + var(--space-sm)) / 2);
height: calc((var(--space-xs) + var(--space-sm)) / 2);
border-radius: 50%;
background: var(--color-success);
flex-shrink: 0;
}
.chat-tool-call-name {
font-family: var(--font-mono, monospace);
flex: 0 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chat-tool-call-preview {
color: var(--text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
min-width: 0;
font-size: 0.6875rem;
}
.chat-tool-call-status-text {
margin-left: auto;
font-size: 0.6875rem;
text-transform: lowercase;
white-space: nowrap;
flex-shrink: 0;
}
.chat-tool-call-content {
margin: var(--space-xs) var(--space-sm) var(--space-sm);
padding: var(--space-sm);
border-radius: var(--radius-sm);
background: var(--bg);
font-family: var(--font-mono, monospace);
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.chat-tool-call-row {
display: grid;
grid-template-columns: auto 1fr;
gap: var(--space-xs);
align-items: start;
}
.chat-tool-call-label {
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.04em;
font-size: 0.6875rem;
}
.chat-tool-call-value {
color: var(--text);
word-break: break-word;
white-space: pre-wrap;
}
.chat-tool-call--running .chat-tool-call-status-dot {
background: var(--color-info);
animation: tool-call-pulse var(--transition-slow) infinite;
}
.chat-tool-call--error summary {
color: var(--color-error);
}
.chat-tool-call--error .chat-tool-call-status-dot {
background: var(--color-error);
}
.chat-tool-call-row--error {
background: color-mix(in srgb, var(--color-error) 10%, transparent);
border-radius: var(--radius-sm);
padding: var(--space-xs);
}
.chat-tool-calls--compact .chat-tool-calls-header,
.chat-tool-calls--compact .chat-tool-calls-group-summary,
.chat-tool-calls--compact .chat-tool-calls-names,
.chat-tool-calls--compact .chat-tool-calls-group-status,
.chat-tool-calls--compact .chat-tool-call summary,
.chat-tool-calls--compact .chat-tool-call-content,
.chat-tool-calls--compact .chat-tool-call-preview,
.chat-tool-calls--compact .chat-tool-call-value,
.chat-tool-calls-group--compact .chat-tool-calls-group-summary,
.chat-tool-calls-group--compact .chat-tool-calls-names {
font-size: 0.6875rem;
}
@keyframes tool-call-pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.4;
}
}
/* Streaming indicator */
.chat-message--streaming {
align-self: flex-start;
background: var(--bg-elevated, var(--bg-secondary));
color: var(--text);
border-bottom-left-radius: 4px;
opacity: 0.9;
}
.chat-pending-message {
display: flex;
align-items: center;
gap: var(--space-sm);
margin-top: var(--space-xs);
padding: var(--space-xs) var(--space-sm);
border-radius: var(--radius-sm);
background: color-mix(in srgb, var(--todo) 10%, transparent);
color: var(--text-muted);
font-size: 12px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.chat-pending-message span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chat-pending-message-dismiss {
background: none;
border: none;
color: var(--text-dim);
cursor: pointer;
padding: var(--space-xs);
line-height: 1;
flex-shrink: 0;
font-size: 14px;
}
.chat-pending-message-dismiss:hover {
color: var(--text-muted);
}
/* === Rescued: classes referenced in QuickChatFAB/ChatView TSX but never defined ====== */
.chat-mention-chip {
display: inline-flex;
align-items: center;
gap: 2px;
padding: 1px 6px;
margin: 0 1px;
background: color-mix(in srgb, var(--todo) 14%, transparent);
color: var(--todo);
border: 1px solid color-mix(in srgb, var(--todo) 30%, transparent);
border-radius: var(--radius-pill);
font-size: 11px;
font-weight: 500;
white-space: nowrap;
}
.quick-chat-panel-message--streaming {
position: relative;
opacity: 0.95;
}
.quick-chat-panel-message--streaming::after {
content: "▍";
display: inline-block;
margin-left: 2px;
color: var(--todo);
animation: quick-chat-cursor-blink 1s steps(1) infinite;
}
@keyframes quick-chat-cursor-blink {
50% { opacity: 0; }
}