/* ── Chat View ─────────────────────────────────────────────────────────────── */ .chat-view { display: flex; flex: 1; width: 100%; height: 100%; width: 100%; flex: 1 1 auto; min-width: 0; overflow: hidden; } /* Sidebar */ .chat-sidebar { min-width: 0; max-width: 500px; /* FN-6210: defensive cap — matches CHAT_SIDEBAR_MAX_WIDTH */ border-right: 1px solid var(--border); display: flex; flex-direction: column; background: var(--bg-secondary); } .chat-sidebar--hidden { display: none; } .chat-sidebar-resize-handle { position: relative; width: var(--space-sm); flex-shrink: 0; cursor: col-resize; background: transparent; touch-action: none; transition: background var(--transition-fast); } .chat-sidebar-resize-handle::before { content: ""; position: absolute; top: 0; bottom: 0; left: 50%; width: var(--space-xs); transform: translateX(-50%); } .chat-sidebar-resize-handle:hover::before, .chat-sidebar-resize-handle:active::before { background: color-mix(in srgb, var(--todo) 30%, transparent); } .chat-sidebar-resize-handle:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-sidebar-scope-toggle { display: flex; gap: var(--space-xs); padding: var(--space-sm) var(--space-md); border-bottom: 1px solid var(--border); } .chat-sidebar-scope-btn { flex: 1; padding: var(--space-sm) var(--space-md); border: none; border-radius: var(--radius-md); background: transparent; color: var(--text-muted); font-size: var(--space-md); transition: background var(--transition-fast), color var(--transition-fast), box-shadow var(--transition-fast); } .chat-sidebar-scope-btn:hover { background: var(--card-hover); color: var(--text); } .chat-sidebar-scope-btn:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-sidebar-scope-btn--active { background: var(--card); color: var(--text); } .chat-sidebar-rooms { flex: 1; min-height: 0; display: flex; flex-direction: column; } .chat-sidebar-rooms-header { padding: var(--space-sm) var(--space-md); border-bottom: 1px solid var(--border); } .chat-sidebar-rooms-empty { flex: 1; display: flex; align-items: center; justify-content: center; text-align: center; color: var(--text-muted); padding: var(--space-xl) var(--space-lg); font-size: var(--space-md); } .chat-room-item { width: 100%; display: flex; align-items: center; justify-content: space-between; gap: var(--space-sm); border: none; border-radius: var(--radius-md); background: transparent; color: var(--text); padding: var(--space-sm) var(--space-md); text-align: left; cursor: pointer; } .chat-room-item:hover { background: var(--card-hover); } .chat-room-item:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-room-item--active { background: color-mix(in srgb, var(--todo) 12%, transparent); } .chat-room-item-details { min-width: 0; display: flex; flex-direction: column; gap: var(--space-xs); } .chat-room-item-name-row { display: inline-flex; align-items: center; } .chat-room-item-name { font-weight: 500; } .chat-unread-dot { display: inline-block; width: var(--space-sm); height: var(--space-sm); border-radius: 50%; background: var(--color-info); flex-shrink: 0; margin-inline-start: var(--space-xs); } .chat-room-item-meta { color: var(--text-muted); font-size: var(--space-sm); } .chat-room-item-delete { color: var(--text-muted); } .chat-room-item-delete:hover, .chat-room-item-delete:focus-visible { color: var(--color-error); } .chat-room-thread-header { display: flex; align-items: center; gap: var(--space-sm); padding: var(--space-md) var(--space-lg); border-bottom: 1px solid var(--border); } .chat-room-thread-members { margin-left: auto; display: flex; gap: var(--space-xs); overflow-x: auto; } .chat-room-empty-pane { display: flex; align-items: center; justify-content: center; color: var(--text-muted); padding: var(--space-2xl); } .chat-sidebar-search-container { padding: var(--space-sm); } .chat-sidebar-search-wrapper { position: relative; display: flex; align-items: center; } .chat-sidebar-search-icon { position: absolute; left: var(--space-md); color: var(--text-dim); pointer-events: none; } .chat-sidebar-search { width: 100%; padding: var(--space-sm) var(--space-md) var(--space-sm) calc(var(--space-2xl) + var(--space-xs)); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--bg); color: var(--text); font-size: 13px; } .chat-session-list { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 8px; } .chat-empty-state { color: var(--text-muted); font-size: 13px; } .chat-empty-state--padded { padding: var(--space-md); } .chat-sidebar-list > .chat-empty-state--padded { flex: 0 0 auto; display: block; text-align: left; } .chat-session-item { padding: 10px 12px; border-radius: var(--radius-md); cursor: pointer; display: flex; flex-direction: column; gap: 2px; transition: background 0.15s; position: relative; } .chat-session-item:hover { background: var(--card-hover); } .chat-session-item--active { background: color-mix(in srgb, var(--todo) 12%, transparent); } .chat-session-item--active:hover { background: color-mix(in srgb, var(--todo) 16%, transparent); } .chat-session-title { font-weight: 500; font-size: 14px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-right: calc(var(--space-md) * 3); } .chat-session-preview { font-size: 12px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-right: calc(var(--space-md) * 3); } .chat-session-meta { display: flex; justify-content: space-between; align-items: center; font-size: 11px; color: var(--text-dim); } .chat-session-meta-model { display: inline-flex; align-items: center; gap: 4px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .chat-session-meta-model .provider-icon { flex-shrink: 0; } /* === Chat Session Delete Button === */ .chat-session-delete-btn { position: absolute; top: var(--space-sm); right: var(--space-sm); display: flex; align-items: center; justify-content: center; width: calc(var(--space-md) * 2); height: calc(var(--space-md) * 2); padding: 0; background: transparent; border: none; border-radius: var(--radius-sm); color: var(--text-muted); cursor: pointer; opacity: 0; transition: opacity var(--transition-fast), color var(--transition-fast); } .chat-session-item:hover .chat-session-delete-btn { opacity: 1; } .chat-session-delete-btn:hover { color: var(--color-error); opacity: 1; } .chat-session-delete-btn:focus { opacity: 1; outline: none; box-shadow: var(--focus-ring-strong); } /* Context menu for session items */ .chat-session-context-menu { position: fixed; background: var(--bg-elevated, var(--bg)); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); padding: var(--space-xs); z-index: 100; } .chat-session-context-menu button { display: flex; align-items: center; gap: var(--space-sm); width: 100%; padding: var(--space-sm) var(--space-md); border: none; background: none; color: var(--text); cursor: pointer; border-radius: var(--radius-sm); font-size: 13px; } .chat-session-context-menu button:hover { background: var(--card-hover); } /* Main chat thread */ .chat-thread { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; position: relative; } .chat-thread-header { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px; } .chat-thread-header-identity { display: inline-flex; align-items: center; gap: var(--space-sm); min-width: 0; } .chat-thread-header-title { font-weight: 600; font-size: 15px; min-width: 0; } .chat-mobile-session-menu { position: relative; min-width: 0; max-width: 100%; } .chat-room-switcher-menu { position: relative; min-width: 0; } .chat-room-switcher-trigger { display: inline-flex; align-items: center; gap: var(--space-xs); border: none; border-radius: var(--radius-sm); background: transparent; color: var(--text); padding: 0; font: inherit; line-height: normal; cursor: pointer; } .chat-room-switcher-trigger:hover { color: var(--text); background: var(--card-hover); } .chat-room-switcher-trigger:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-room-switcher-trigger > svg { color: var(--text-muted); flex-shrink: 0; } .chat-room-switcher-dropdown { position: absolute; top: calc(100% + var(--space-xs)); left: 0; min-width: calc(var(--space-xl) * 6); display: flex; flex-direction: column; gap: var(--space-xs); padding: var(--space-xs); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); box-shadow: var(--shadow-lg); z-index: 4; max-height: calc(var(--space-xl) * 10); overflow-y: auto; } .chat-room-switcher-option { width: 100%; display: flex; align-items: center; border: none; border-radius: var(--radius-sm); background: transparent; color: var(--text); cursor: pointer; text-align: left; padding: var(--space-sm) var(--space-md); line-height: normal; } .chat-room-switcher-option:hover { background: var(--card-hover); } .chat-room-switcher-option:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-room-switcher-option--active { background: color-mix(in srgb, var(--todo) 12%, transparent); } .chat-mobile-session-trigger { width: 100%; max-width: 100%; min-width: 0; display: inline-flex; align-items: center; justify-content: flex-start; gap: var(--space-sm); border: none; border-radius: var(--radius-md); background: transparent; color: var(--text); padding: var(--space-xs) var(--space-sm); min-height: calc(var(--space-lg) * 2 + var(--space-xs)); font: inherit; line-height: normal; text-align: left; } .chat-mobile-session-trigger .chat-thread-header-title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .chat-mobile-session-trigger .chat-model-tag { flex-shrink: 0; max-width: calc(var(--space-xl) * 4); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .chat-mobile-session-trigger .provider-icon, .chat-mobile-session-trigger > svg:first-child { flex-shrink: 0; } .chat-mobile-session-trigger > svg { width: var(--icon-size-md); height: var(--icon-size-md); } .chat-mobile-session-trigger svg:last-child { margin-left: auto; color: var(--text-muted); flex-shrink: 0; } .chat-mobile-session-trigger:hover { background: var(--card-hover); } .chat-mobile-session-trigger:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-mobile-session-dropdown { position: absolute; top: calc(100% + var(--space-xs)); left: 0; right: 0; display: flex; flex-direction: column; gap: var(--space-xs); padding: var(--space-xs); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); box-shadow: var(--shadow-lg); z-index: 4; max-height: calc(var(--space-xl) * 10); overflow-y: auto; } .chat-mobile-session-option { width: 100%; display: flex; align-items: flex-start; min-height: calc(var(--space-lg) * 2.25); border: none; border-radius: var(--radius-sm); background: transparent; color: var(--text); cursor: pointer; text-align: left; padding: var(--space-sm) var(--space-md); line-height: normal; } .chat-mobile-session-option:hover { background: var(--card-hover); } .chat-mobile-session-option:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-mobile-session-option--active { background: color-mix(in srgb, var(--todo) 12%, transparent); } .chat-mobile-session-option-title { width: 100%; display: block; line-height: normal; white-space: normal; overflow-wrap: anywhere; } .chat-thread-header-new-chat { flex-shrink: 0; } /* Single thread-wide markdown / plain-text toggle, anchored to the right of * the header next to "New Chat". Replaces the per-message eye toggle that * used to live inside every assistant bubble. */ .chat-thread-header-render-toggle { margin-left: auto; display: inline-flex; align-items: center; justify-content: center; width: calc(var(--space-md) * 3); height: calc(var(--space-md) * 3); min-width: calc(var(--space-md) * 3); min-height: calc(var(--space-md) * 3); padding: 0; border: none; border-radius: var(--radius-sm); color: var(--text-muted); background: transparent; cursor: pointer; outline: none; transition: background var(--transition-fast), color var(--transition-fast); } .chat-thread-header-render-toggle:hover { background: var(--bg-hover, var(--bg-secondary)); color: var(--text); } .chat-thread-header-render-toggle:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-thread-header-render-toggle--plain { color: var(--text); } /* Messages */ .chat-messages { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: var(--space-lg); display: flex; flex-direction: column; gap: var(--space-md); -webkit-overflow-scrolling: touch; touch-action: pan-y; /* Stop iOS rubber-band scroll from propagating to the parent / document when the user swipes on the edge of the messages list with the keyboard up. Without this, the bounce escapes the scroll container, the page itself shifts, and the chat-thread transform momentarily exposes the body background as a gray box partially covering the messages. */ overscroll-behavior: contain; } .chat-messages > .chat-empty-state { flex: 1 1 auto; width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-md); padding: var(--space-xl) var(--space-lg); border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); text-align: center; box-sizing: border-box; } .chat-message { max-width: 75%; padding: 10px 14px; border-radius: 12px; font-size: 14px; line-height: 1.5; word-break: break-word; } .chat-jump-to-latest { position: absolute; left: 50%; bottom: calc(var(--space-xl) * 3); transform: translateX(-50%); z-index: 2; } .chat-message--user { align-self: flex-end; background: var(--accent); color: var(--accent-text); border-bottom-right-radius: var(--radius-sm); } .chat-message--assistant { align-self: flex-start; background: var(--bg-elevated, var(--bg-secondary)); color: var(--text); border-bottom-left-radius: var(--radius-sm); } .chat-message--failure { background: var(--status-error-bg); border: var(--btn-border-width) solid var(--status-error-bg-deep); } .chat-message-avatar { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; font-size: 12px; color: var(--text-muted); } /* Model-only chats hide the per-message agent identity. The toggle still * needs a place to live, so collapse the avatar row to just the toggle on * the right edge instead of a full identity strip. */ .chat-message-avatar.chat-message-avatar--toolbar-only { margin-bottom: 0; min-height: 0; justify-content: flex-end; } .chat-message-render-toggle { display: inline-flex; align-items: center; justify-content: center; margin-left: auto; width: calc(var(--space-md) * 3); height: calc(var(--space-md) * 3); min-width: calc(var(--space-md) * 3); min-height: calc(var(--space-md) * 3); padding: 0; border: none; color: var(--text-muted); background: transparent; cursor: pointer; outline: none; } .chat-message--assistant .chat-message-render-toggle { opacity: 0.6; transition: opacity var(--transition-fast); } .chat-message--assistant:hover .chat-message-render-toggle, .chat-message-render-toggle:focus-visible, .chat-message-render-toggle.chat-message-render-toggle--plain { opacity: 1; } .chat-message-render-toggle:hover { color: var(--text); background: color-mix(in srgb, var(--surface) 55%, transparent); } .chat-message-render-toggle:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-message-render-toggle--plain { color: var(--text); } .chat-model-tag { font-size: 11px; padding: 1px 6px; border-radius: 4px; background: var(--surface-bg, color-mix(in srgb, var(--surface) 55%, transparent)); color: var(--text-muted); margin-left: 6px; white-space: nowrap; vertical-align: middle; } .chat-message-content { white-space: pre-wrap; } .chat-message-content--plain { white-space: pre-wrap; } .chat-message-content--markdown { white-space: normal; overflow-wrap: anywhere; } .chat-message-content--failure { display: flex; flex-direction: column; gap: var(--space-sm); } .chat-message-failure-summary-row { display: flex; align-items: center; gap: var(--space-sm); color: var(--color-error); font-weight: 600; } .chat-message-failure-label { min-width: 0; } .chat-message-failure-summary { color: var(--text); white-space: pre-wrap; } .chat-message-failure-badges { display: flex; flex-wrap: wrap; gap: var(--space-xs); } .chat-message-failure-badge { display: inline-flex; align-items: center; min-height: calc(var(--space-lg) * 1.5); padding: 0 var(--space-sm); border-radius: var(--radius-pill); background: var(--status-error-bg-deep); color: var(--color-error); font-family: var(--font-mono); font-size: calc(var(--space-sm) + var(--space-xs)); } .chat-message-failure-details { border: var(--btn-border-width) solid var(--status-error-bg-deep); border-radius: var(--radius-md); background: var(--status-error-bg-deep); } .chat-message-failure-details summary { display: flex; align-items: center; gap: var(--space-sm); cursor: pointer; list-style: none; padding: var(--space-sm) var(--space-md); color: var(--text-muted); } .chat-message-failure-details summary::-webkit-details-marker { display: none; } .chat-message-failure-details summary:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-message-failure-detail { margin: 0; padding: 0 var(--space-md) var(--space-md); color: var(--text); font-family: var(--font-mono); white-space: pre-wrap; } .chat-message-failure-reference { display: flex; flex-wrap: wrap; gap: var(--space-sm); align-items: center; padding: 0 var(--space-md) var(--space-md); color: var(--text-muted); } .chat-message-failure-reference-label { font-weight: 600; } .chat-message-failure-reference-value { min-width: 0; overflow-wrap: anywhere; } .chat-message-failure-reference-link { margin-left: auto; border-color: var(--status-error-bg-deep); background: var(--status-error-bg-deep); color: var(--color-error); text-decoration: none; } .chat-message-failure-reference-link:hover, .chat-message-failure-reference-link:focus-visible { border-color: var(--color-error); background: var(--status-error-bg); color: var(--color-error); } .chat-message-failure-reference-details { margin-left: auto; } .chat-message-failure-reference-details summary { list-style: none; } .chat-message-failure-reference-details summary::-webkit-details-marker { display: none; } .chat-message-failure-reference-meta { display: grid; gap: var(--space-sm); margin: var(--space-sm) 0 0; padding: var(--space-sm); border: var(--btn-border-width) solid var(--status-error-bg-deep); border-radius: var(--radius-md); background: var(--status-error-bg); } .chat-message-failure-reference-meta div { display: grid; gap: var(--space-xs); } .chat-message-failure-reference-meta dt { color: var(--text-muted); font-size: calc(var(--space-sm) + var(--space-xs)); font-weight: 600; } .chat-message-failure-reference-meta dd { margin: 0; color: var(--text); font-family: var(--font-mono); overflow-wrap: anywhere; } .chat-message-actions { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-xs); margin-top: var(--space-xs); } .chat-message-copy-action, .chat-message-scroll-to-top-action { display: flex; align-items: center; justify-content: center; margin-top: var(--space-xs); margin-left: auto; width: calc(var(--space-lg) * 2); height: calc(var(--space-lg) * 2); min-width: calc(var(--space-lg) * 2); min-height: calc(var(--space-lg) * 2); padding: 0; border: none; border-radius: var(--radius-sm); background: color-mix(in srgb, var(--surface) 35%, transparent); color: var(--text-muted); cursor: pointer; opacity: 0.85; transition: opacity var(--transition-fast), color var(--transition-fast), background var(--transition-fast); } .chat-message-actions .chat-message-copy-action, .chat-message-actions .chat-message-scroll-to-top-action { margin-top: 0; margin-left: 0; } .chat-message-copy-action:hover, .chat-message-scroll-to-top-action:hover { opacity: 1; color: var(--text); background: color-mix(in srgb, var(--surface) 55%, transparent); } .chat-message-copy-action:focus-visible, .chat-message-scroll-to-top-action:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); opacity: 1; } .chat-message-copy-action--success { color: var(--color-success); } .chat-message-copy-action--error { color: var(--color-error); } .chat-message-content--markdown > :first-child { margin-top: 0; } .chat-message-content--markdown > :last-child { margin-bottom: 0; } .chat-message-content--markdown p, .chat-message-content--markdown ul, .chat-message-content--markdown ol, .chat-message-content--markdown blockquote, .chat-message-content--markdown pre, .chat-message-content--markdown table { margin: 0 0 var(--space-sm); } .chat-message-content--markdown ul, .chat-message-content--markdown ol { padding-left: var(--space-lg); } .chat-message-content--markdown code { font-family: var(--font-mono); font-size: 0.8125rem; } .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); } .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%; } .chat-markdown-table { display: block; width: 100%; max-width: 100%; overflow-x: auto; border-collapse: collapse; } .chat-markdown-table th, .chat-markdown-table td { border: 1px solid color-mix(in srgb, var(--border) 85%, transparent); padding: var(--space-xs) var(--space-sm); } .chat-message-time { font-size: 11px; color: var(--text-dim); margin-top: 4px; } .chat-message-thinking { margin-top: 6px; } .chat-message-thinking summary { font-size: 12px; color: var(--text-muted); cursor: pointer; } .chat-message-thinking-content { font-size: 13px; color: var(--text-muted); 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-xs); list-style: none; cursor: pointer; padding: var(--space-xs); color: var(--text-muted); border-radius: var(--radius-sm); font-size: var(--space-md); min-width: 0; } .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; } .chat-tool-calls-group > .chat-tool-call { margin: 0 var(--space-xs) var(--space-xs); } .chat-tool-calls-group > .chat-tool-call:last-child { margin-bottom: var(--space-xs); } .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); color: var(--text-muted); border-radius: var(--radius-sm); font-size: var(--space-md); min-width: 0; } .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; } .chat-tool-call-content { margin: var(--space-xs); 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-typing-indicator { display: inline-flex; gap: 4px; padding: 4px 0; } .chat-typing-indicator span { width: 6px; height: 6px; border-radius: 50%; background: var(--text-muted); animation: chat-typing-bounce 1.4s infinite ease-in-out; } .chat-typing-indicator span:nth-child(1) { animation-delay: 0s; } .chat-typing-indicator span:nth-child(2) { animation-delay: 0.2s; } .chat-typing-indicator span:nth-child(3) { animation-delay: 0.4s; } @keyframes chat-typing-bounce { 0%, 80%, 100% { opacity: 0.3; } 40% { opacity: 1; } } /* Chat waiting state */ .chat-message-content--waiting { color: var(--text-muted); font-style: italic; font-size: 13px; } .quick-chat-panel-waiting { color: var(--text-muted) !important; font-style: italic; } /* Input area */ .chat-input-area { position: relative; padding: var(--space-md) var(--space-lg); border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: var(--space-sm); /* Stop iOS rubber-band escape when the user swipes on the composer edge with the keyboard up. Without it the page momentarily shifts and the body background peeks through above the messages. */ overscroll-behavior: contain; } .chat-input-row { --chat-input-control-size: calc(var(--space-lg) * 2.5); display: flex; align-items: flex-end; gap: var(--space-sm); width: 100%; } /* === Chat Skill Menu === */ .chat-skill-menu { position: absolute; left: var(--space-lg); bottom: calc(100% + var(--space-xs)); min-width: calc((var(--space-xl) * 10) + var(--space-2xl) + (var(--space-xs) * 2)); max-width: calc(100% - (var(--space-lg) * 2)); max-height: calc(var(--space-xl) * 10); overflow-y: auto; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-lg); z-index: 50; } .chat-skill-menu-item { width: 100%; border: none; background: transparent; color: inherit; text-align: left; padding: var(--space-sm) var(--space-md); cursor: pointer; display: flex; flex-direction: column; gap: var(--space-xs); transition: background var(--transition-fast); } .chat-skill-menu-item:hover, .chat-skill-menu-item--highlighted { background: var(--card-hover); } .chat-skill-menu-item:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); background: var(--card-hover); } .chat-skill-menu-item-name { font-size: 0.8125rem; color: var(--text); font-family: var(--font-mono); } .chat-skill-menu-item-description { font-size: 0.75rem; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: calc((var(--space-xl) * 10) + var(--space-lg) + var(--space-xs)); } .chat-skill-menu-empty { padding: var(--space-sm) var(--space-md); color: var(--text-muted); font-size: 0.8125rem; } .chat-input-textarea { /* FN-5322: this textarea sits directly inside `.chat-input-wrapper`, which is a column flex container (via AgentMentionPopup.css). Keep it out of vertical flex sizing so the inline autosize height from ChatView.tsx is honored instead of collapsing back toward the one-line minimum. */ flex: 0 0 auto; width: 100%; box-sizing: border-box; -webkit-appearance: none; appearance: none; resize: none; overflow-y: hidden; border: 1px solid var(--border); border-radius: 12px; padding: 10px 14px; background: var(--bg); color: var(--text); font-size: 14px; font-family: inherit; line-height: 1.4; max-height: 640px; min-height: 40px; /* Mobile keeps the desktop 640px cap because the surrounding chat-input-area and thread flex bounds already prevent the composer from overtaking the message pane before textarea scrolling engages. */ /* touch-action: manipulation allows tap-to-focus (so iOS doesn't auto-dismiss the keyboard between gesture frames) and blocks double-tap zoom + pan/zoom gestures. We previously tried `none` to lock the composer against pan, but that broke first-tap focus on iOS. */ touch-action: manipulation; overscroll-behavior: contain; } /* FN-6212: On tablet viewports (769–1024px), cap the composer to 200px so it doesn't consume most of the visible thread. Desktop keeps 640px; mobile is already flex-constrained by the chat-thread layout. */ @media (min-width: 769px) and (max-width: 1024px) { .chat-input-textarea { max-height: 200px; } } .chat-input-textarea:focus { outline: none; border-color: var(--accent); } .chat-input-send { width: var(--chat-input-control-size); min-height: var(--chat-input-control-size); border-radius: 50%; border: none; background: var(--accent); color: var(--accent-text); cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .chat-input-send:disabled { opacity: 0.4; cursor: not-allowed; } .chat-input-stop { width: var(--chat-input-control-size); min-height: var(--chat-input-control-size); border-radius: var(--radius-md); border: none; background: color-mix(in srgb, var(--color-error) 15%, transparent); color: var(--color-error); cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition: background var(--transition-fast), transform var(--transition-fast), color var(--transition-fast); } .chat-input-stop:hover { background: color-mix(in srgb, var(--color-error) 25%, transparent); } .chat-input-stop:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-input-stop:active { transform: scale(0.97); } .chat-attach-btn { min-width: calc(var(--space-lg) * 2); min-height: calc(var(--space-lg) * 2); align-self: flex-end; } .chat-input-wrapper { flex: 1; } .chat-input-wrapper--dragover { border: 1px dashed var(--todo); border-radius: var(--radius-md); } .chat-attachment-previews { display: flex; gap: var(--space-xs); padding: var(--space-sm) var(--space-md); overflow-x: auto; border-top: 1px solid var(--border); background: var(--surface); width: 100%; } .chat-attachment-preview { width: calc(var(--space-xl) * 3); height: calc(var(--space-xl) * 3); border-radius: var(--radius-sm); border: 1px solid var(--border); position: relative; flex-shrink: 0; overflow: hidden; display: flex; align-items: center; justify-content: center; } .chat-attachment-preview img { width: 100%; height: 100%; object-fit: cover; } .chat-attachment-preview-name { font-size: 0.625rem; text-align: center; overflow: hidden; text-overflow: ellipsis; color: var(--text-muted); padding: var(--space-xs); } .chat-attachment-remove { position: absolute; top: calc(var(--space-xs) * -1); right: calc(var(--space-xs) * -1); background: color-mix(in srgb, var(--color-error) 80%, transparent); color: var(--text); border: none; border-radius: var(--radius-pill); width: calc(var(--space-md) * 2); height: calc(var(--space-md) * 2); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: background var(--transition-fast), transform var(--transition-fast); } .chat-attachment-remove:hover { background: color-mix(in srgb, var(--color-error) 90%, transparent); } .chat-attachment-remove:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } .chat-attachment-remove:active { transform: scale(0.97); } .chat-message-attachments { display: flex; flex-wrap: wrap; gap: var(--space-xs); margin-top: var(--space-xs); } .chat-message-attachment-link { display: inline-flex; border-radius: var(--radius-sm); } .chat-message-attachment { max-width: calc(var(--space-xl) * 12); border-radius: var(--radius-sm); } .chat-message-attachment-file { display: inline-flex; align-items: center; gap: var(--space-xs); padding: var(--space-xs) var(--space-sm); border-radius: var(--radius-sm); background: color-mix(in srgb, var(--surface) 50%, transparent); font-size: 0.75rem; color: var(--text-muted); text-decoration: none; } .chat-message-attachment-file:hover { background: color-mix(in srgb, var(--surface) 70%, transparent); } .chat-message-attachment-file:focus-visible, .chat-message-attachment-link:focus-visible { outline: none; box-shadow: var(--focus-ring-strong); } /* === Chat Pending Message === */ .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); } /* Sidebar footer contains the primary chat-scope action on desktop and mobile. */ .chat-sidebar-footer { display: block; padding: var(--space-sm) var(--space-md); border-top: 1px solid var(--border); background: var(--bg-secondary); } .chat-sidebar-footer .chat-sidebar-footer-btn { width: 100%; justify-content: center; min-height: 40px; } /* === Mobile: one pane at a time ========================================= Sidebar (session list) and thread are siblings of .chat-view. On mobile we show whichever one is "active" full-width: when the sidebar is visible, the thread is hidden; when the sidebar is hidden (a session is open), the sidebar is collapsed via .chat-sidebar--hidden and the thread takes the full viewport. The thread already renders a back button (ChevronLeft) on mobile to flip back to the session list. */ @media (max-width: 768px) { .chat-view { flex-direction: column; } .chat-sidebar { width: 100%; min-width: 100%; max-width: 100%; /* FN-6210: mobile sidebar spans full viewport */ height: 100%; max-height: none; border-right: none; border-bottom: 1px solid var(--border); flex-direction: column; } .chat-sidebar-header { display: none; } .chat-sidebar-search { min-height: calc(var(--space-2xl) + var(--space-xs)); } .chat-sidebar-list { flex: 1; min-height: 0; overflow-y: auto; } /* When the sidebar is shown, the thread is hidden so only the session list is visible. When the sidebar is hidden, the thread fills the viewport. Adjacent-sibling selector keeps logic in CSS only. */ .chat-sidebar:not(.chat-sidebar--hidden) + .chat-thread { display: none; } .chat-thread--keyboard-active { height: calc(var(--vv-height, calc(100dvh - var(--keyboard-overlap, 0px))) - var(--header-height)); max-height: calc(var(--vv-height, calc(100dvh - var(--keyboard-overlap, 0px))) - var(--header-height)); /* NOTE: the translateY drift compensation is applied imperatively in JS (see ChatView's vv `apply()`), NOT here. Declaring `transform`/`will-change: transform` in CSS keeps a non-`none` transform on .chat-thread for the entire keyboard-active window — and since .chat-thread is an ancestor of the focused composer textarea, iOS Safari treats establishing that containing block as a reason to blur the input and collapse the keyboard the instant it opens. JS only sets a transform when there is real viewport drift (offsetTop > 0); at the focus moment offsetTop is 0, so the ancestor stays `transform: none` and the keyboard stays up. */ } /* On mobile, the active scope affordance uses a full-width pinned footer. */ .chat-sidebar-footer { display: block; padding: var(--space-sm) var(--space-md); padding-bottom: max(var(--space-sm), env(safe-area-inset-bottom, 0)); border-top: 1px solid var(--border); background: var(--bg-secondary); } .chat-sidebar-footer .chat-sidebar-footer-btn { width: 100%; justify-content: center; min-height: 40px; } .chat-thread-header { flex-wrap: nowrap; } .chat-messages { min-height: 0; overflow-y: auto; padding: var(--space-lg) var(--space-sm); -webkit-overflow-scrolling: touch; touch-action: pan-y; } .chat-messages > .chat-empty-state { padding: var(--space-xl) var(--space-sm); border: none; border-radius: 0; background: transparent; box-sizing: border-box; } .chat-thread-header-identity { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: visible; } .chat-mobile-session-menu { width: 100%; } .chat-room-switcher-dropdown { left: 0; right: auto; min-width: calc(var(--space-xl) * 5); max-width: min(calc(var(--space-xl) * 10), calc(100vw - var(--space-2xl))); } .chat-thread-header-identity .chat-thread-header-title, .chat-thread-header-identity .chat-model-tag { overflow: hidden; text-overflow: ellipsis; } .chat-thread-header-render-toggle { flex-shrink: 0; } .chat-thread-header-new-chat { display: none; } .chat-sidebar-scope-btn { min-height: calc(var(--space-lg) * 2.25); } /* FN-4352: secondary control inside tappable session row; keep compact size (inflated by FN-3628). */ .chat-session-delete-btn { opacity: 1; } /* FN-4352/FN-4385: keep row text clearance aligned to compact secondary delete control (matches base compact clearance). */ .chat-session-title, .chat-session-preview { padding-right: calc(var(--space-md) * 3); } .chat-message--assistant .chat-message-render-toggle { opacity: 1; } /* FN-4352: secondary hover-revealed row action keeps base compact sizing (inflated by FN-3628). */ .chat-message-copy-action { opacity: 1; } .chat-message-scroll-to-top-action { opacity: 1; } .chat-tool-calls-group-summary, .chat-tool-call summary { flex-wrap: nowrap; flex-direction: row; align-items: center; } .chat-tool-calls-names, .chat-tool-call-name, .chat-tool-call-status-text, .chat-tool-calls-group-status, .chat-tool-calls-count { white-space: nowrap; } .chat-tool-calls-group-status, .chat-tool-call-status-text { margin-left: auto; } /* primary touch targets per AGENTS.md convention */ .chat-attach-btn { min-width: calc(var(--space-lg) * 2.25); min-height: calc(var(--space-lg) * 2.25); } .chat-input-send, .chat-input-stop { min-width: var(--chat-input-control-size); min-height: var(--chat-input-control-size); } .chat-attachment-preview { width: calc(var(--space-xl) * 2.5); height: calc(var(--space-xl) * 2.5); } .chat-attachment-remove { width: calc(var(--space-lg) * 2.25); height: calc(var(--space-lg) * 2.25); } .chat-message { max-width: 90%; } .chat-message-failure-details summary, .chat-message-failure-reference { align-items: flex-start; flex-wrap: wrap; } .chat-message-failure-reference-link { margin-left: 0; } .chat-new-dialog { width: 95vw; margin: var(--space-lg); } .chat-thread-header { padding: var(--space-sm) var(--space-md); } .chat-skill-menu { left: var(--space-md); right: calc(var(--space-md) + var(--mobile-nav-height)); min-width: 0; max-width: none; } .chat-jump-to-latest { bottom: calc(var(--space-xl) * 4); } .chat-sidebar-rooms-header .btn { width: 100%; justify-content: center; } .chat-room-thread-header { padding: var(--space-sm) var(--space-md); } .chat-room-thread-header .btn-icon { min-height: 36px; min-width: 36px; } .chat-room-item { padding: var(--space-sm); } .chat-room-item-delete { flex-shrink: 0; } .chat-room-thread-members { max-width: 50%; } } .chat-mention-chip { display: inline-flex; align-items: center; gap: var(--space-xs); padding: var(--space-xs) var(--space-sm); margin: 0 var(--space-xs); background: color-mix(in srgb, var(--todo) 14%, transparent); color: var(--todo); border: var(--btn-border-width, 1px) solid color-mix(in srgb, var(--todo) 30%, transparent); border-radius: var(--radius-pill); font-size: calc(var(--space-sm) + var(--space-xs) * 0.5); font-weight: 500; white-space: nowrap; } .chat-mention-chip--non-member { background: color-mix(in srgb, var(--color-warning) 12%, transparent); color: var(--text-muted); border-color: color-mix(in srgb, var(--color-warning) 35%, transparent); } .chat-message--user .chat-mention-chip { color: var(--accent-text); background: color-mix(in srgb, var(--accent-text) 18%, transparent); border-color: color-mix(in srgb, var(--accent-text) 40%, transparent); } .chat-message--user .chat-mention-chip--non-member { background: color-mix(in srgb, var(--color-warning) 30%, transparent); color: var(--accent-text); border-color: color-mix(in srgb, var(--color-warning) 55%, transparent); }