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>
1098 lines
26 KiB
CSS
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; }
|
|
}
|
|
|