* fix(desktop): prevent IME Enter from splitting messages and viewport resize from disarming scroll anchor Two fixes for the Hermes Desktop composer: 1. IME composition Enter was treated as message submission. When a Korean/ Japanese/Chinese IME is composing text and the user presses Enter to finalise the preedit, handleEditorKeyDown fired submitDraft() because it did not check event.nativeEvent.isComposing. The assistant-ui hidden textarea already guards this correctly; the custom contentEditable handler was missing it. Added an early return when isComposing is true. 2. Viewport resize (composer expand/collapse, window resize) was disarming the scroll sticky-bottom anchor. When the composer grows, the thread viewport shrinks, the browser adjusts scrollTop down to keep content visible, and the onScroll handler misread this as a user scroll-up. Added lastClientHeightRef tracking so the disarm condition now requires BOTH stable scrollHeight AND stable clientHeight before treating a scrollTop decrease as user intent. Fixes: random mid-message sends during IME typing; scroll jumps when the composer resizes or the window changes size. * fix(desktop): prevent virtualizer measurement adjustments from fighting scroll anchoring The virtualizer's measureElement callbacks trigger scroll adjustments when item sizes differ from estimates. These fight our ResizeObserver + pinToBottom loop, creating visible rubber-banding (view snaps to composer then jumps back up), even during idle. Three changes: 1. React.memo on VirtualizedThread to stop parent re-renders cascading 2. Shared stickyBottomRef so scrollToFn can check bottom state 3. scrollToFn override: skip adjustments when user is at bottom * fix(desktop): use stable useCallback ref instead of inline arrow for onBranchInNewChat The inline arrow `messageId => void branchInNewChat(messageId)` created a new function reference on every render. This cascaded through: desktop-controller → ChatView → Thread → useMemo([...onBranchInNewChat]) → new messageComponents object → VirtualizedThread receives new prop → React.memo overridden → virtualizer recalculates → measurement adjustments trigger scroll jumps at the 15-second useStatusSnapshot interval. Pass the already-useCallback'd branchInNewChat directly. * fix(desktop): use ctrlEnter submitMode on hidden textarea + gate ResizeObserver on isRunning Two root-cause fixes: 1. IME message splitting: The hidden ComposerPrimitive.Input textarea had submitMode='enter' (default), so any Enter keydown it received — even during IME composition — triggered form.requestSubmit(). Changed to submitMode='ctrlEnter' so only the contentEditable div (which correctly checks isComposing) handles plain-Enter submission. 2. Scroll jumps during idle: The ResizeObserver auto-follow loop was active even when the thread wasn't running, causing spurious pinToBottom calls whenever any layout shift occurred (browser reflow, font load, GPU cache eviction). Gated the ResizeObserver on thread.isRunning so auto-scroll only follows during active streaming. User messages still pin via useLayoutEffect, and thread.runStart still calls jumpToBottom. * fix(desktop): keep chat bottom anchor stable through idle layout shifts * fix(desktop): prevent code block shrink scroll bounce * fix(desktop): release bottom height lock on run completion * fix(desktop): keep streaming code blocks rendered * fix(desktop): keep bottom anchored through final render * fix(desktop): render streaming reasoning code blocks * feat(desktop): add subtle streaming block animations
1101 lines
35 KiB
CSS
1101 lines
35 KiB
CSS
@import 'tailwindcss';
|
|
@plugin '@tailwindcss/typography';
|
|
@import 'tw-shimmer';
|
|
@import 'katex/dist/katex.min.css';
|
|
@import '@vscode/codicons/dist/codicon.css';
|
|
@custom-variant dark (&:is(.dark *));
|
|
|
|
@font-face {
|
|
font-family: 'Collapse';
|
|
font-style: normal;
|
|
font-weight: 700;
|
|
font-display: swap;
|
|
src: url('../../../node_modules/@nous-research/ui/dist/fonts/Collapse-Bold.woff2') format('woff2');
|
|
}
|
|
|
|
@theme inline {
|
|
--color-background: var(--dt-background);
|
|
--color-foreground: var(--dt-foreground);
|
|
--color-card: var(--dt-card);
|
|
--color-card-foreground: var(--dt-card-foreground);
|
|
--color-muted: var(--dt-muted);
|
|
--color-muted-foreground: var(--dt-muted-foreground);
|
|
--color-popover: var(--dt-popover);
|
|
--color-popover-foreground: var(--dt-popover-foreground);
|
|
--color-primary: var(--dt-primary);
|
|
--color-primary-foreground: var(--dt-primary-foreground);
|
|
--color-secondary: var(--dt-secondary);
|
|
--color-secondary-foreground: var(--dt-secondary-foreground);
|
|
--color-accent: var(--dt-accent);
|
|
--color-accent-foreground: var(--dt-accent-foreground);
|
|
--color-border: var(--dt-border);
|
|
--color-input: var(--dt-input);
|
|
--color-ring: var(--dt-ring);
|
|
--color-destructive: var(--dt-destructive);
|
|
--color-destructive-foreground: var(--dt-destructive-foreground);
|
|
|
|
--color-midground: var(--dt-midground);
|
|
--color-midground-foreground: var(--dt-midground-foreground);
|
|
|
|
--font-sans: var(--dt-font-sans);
|
|
--font-mono: var(--dt-font-mono);
|
|
|
|
--spacing-mul: var(--dt-spacing-mul, 1);
|
|
|
|
--radius-xs: calc(var(--radius-scalar) * 0.125rem);
|
|
--radius-sm: calc(var(--radius-scalar) * 0.5rem);
|
|
--radius-md: calc(var(--radius-scalar) * 0.625rem);
|
|
--radius-lg: calc(var(--radius-scalar) * 0.75rem);
|
|
--radius-xl: calc(var(--radius-scalar) * 1rem);
|
|
--radius-2xl: calc(var(--radius-scalar) * 1.5rem);
|
|
--radius-3xl: calc(var(--radius-scalar) * 2rem);
|
|
--radius-4xl: calc(var(--radius-scalar) * 2.5rem);
|
|
|
|
--color-sidebar-ring: var(--sidebar-ring);
|
|
--color-sidebar-border: var(--sidebar-border);
|
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
--color-sidebar-accent: var(--sidebar-accent);
|
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
--color-sidebar-primary: var(--sidebar-primary);
|
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
--color-sidebar: var(--sidebar);
|
|
|
|
--shadow-ink: var(--dt-foreground);
|
|
--shadow-xs: 0 0.0625rem 0.125rem color-mix(in srgb, #000 5%, transparent);
|
|
--shadow-sm:
|
|
0 0 0 0.0625rem color-mix(in srgb, var(--dt-foreground) 6%, transparent),
|
|
0 0.125rem 0.5rem color-mix(in srgb, #000 4%, transparent);
|
|
--shadow-md:
|
|
0 0 0 0.0625rem color-mix(in srgb, var(--dt-foreground) 8%, transparent),
|
|
0 0.25rem 1rem color-mix(in srgb, #000 8%, transparent),
|
|
0 1rem 2rem -1.5rem color-mix(in srgb, #000 18%, transparent);
|
|
--shadow-lg:
|
|
inset 0 0.0625rem 0 color-mix(in srgb, #fff 28%, transparent),
|
|
0 0 0 0.0625rem color-mix(in srgb, var(--dt-foreground) 8%, transparent),
|
|
0 0.75rem 2rem color-mix(in srgb, #000 12%, transparent);
|
|
--shadow-header:
|
|
0 0.0625rem 0 color-mix(in srgb, var(--dt-foreground) 7%, transparent),
|
|
0 0.625rem 1.5rem -1.25rem color-mix(in srgb, #000 16%, transparent);
|
|
--shadow-composer: 0 0.0625rem 0.125rem color-mix(in srgb, #000 5%, transparent);
|
|
--shadow-composer-focus:
|
|
0 0 0 0.125rem color-mix(in srgb, var(--dt-composer-ring) calc(10% * var(--composer-ring-strength)), transparent),
|
|
0 0 0 0.0625rem color-mix(in srgb, var(--dt-composer-ring) calc(22% * var(--composer-ring-strength)), transparent),
|
|
0 0.25rem 0.875rem color-mix(in srgb, #000 8%, transparent),
|
|
0 0.75rem 2rem -1.25rem color-mix(in srgb, #000 14%, transparent);
|
|
}
|
|
|
|
@layer base {
|
|
:root {
|
|
color-scheme: light;
|
|
|
|
--theme-foreground: #17171a;
|
|
--theme-primary: #0053fd;
|
|
--theme-secondary: color-mix(in srgb, #0053fd 7%, #ffffff);
|
|
--theme-accent-soft: color-mix(in srgb, #0053fd 10%, #ffffff);
|
|
--theme-midground: #0053fd;
|
|
--theme-warm: #cf806d;
|
|
--theme-background-seed: #f8faff;
|
|
--theme-sidebar-seed: #f3f7ff;
|
|
--theme-card-seed: #ffffff;
|
|
--theme-elevated-seed: #ffffff;
|
|
--theme-bubble-seed: color-mix(in srgb, #0053fd 6%, #ffffff);
|
|
--theme-neutral-chrome: #f3f3f3;
|
|
--theme-neutral-sidebar: #f3f3f3;
|
|
--theme-neutral-card: #fcfcfc;
|
|
--theme-mix-chrome: 92%;
|
|
--theme-mix-sidebar: 100%;
|
|
--theme-mix-card: 22%;
|
|
--theme-mix-elevated: 28%;
|
|
--theme-mix-bubble: 0%;
|
|
--theme-fill-primary-accent-mix: 16%;
|
|
--theme-fill-secondary-accent-mix: 11%;
|
|
--theme-fill-tertiary-accent-mix: 8%;
|
|
--theme-fill-quaternary-accent-mix: 5%;
|
|
--theme-fill-quinary-accent-mix: 3%;
|
|
--theme-stroke-primary-accent-mix: 24%;
|
|
--theme-stroke-secondary-accent-mix: 16%;
|
|
--theme-stroke-tertiary-accent-mix: 10%;
|
|
--theme-stroke-quaternary-accent-mix: 6%;
|
|
--theme-row-hover-accent-mix: 4%;
|
|
--theme-row-active-accent-mix: 8%;
|
|
--theme-control-hover-accent-mix: 6%;
|
|
--theme-control-active-accent-mix: 8%;
|
|
|
|
--ui-base: var(--theme-foreground);
|
|
--ui-accent: var(--theme-midground);
|
|
--ui-accent-secondary: var(--theme-primary);
|
|
--ui-warm: var(--theme-warm);
|
|
--ui-red: #cf2d56;
|
|
--ui-orange: #db704b;
|
|
--ui-yellow: #c08532;
|
|
--ui-green: #1f8a65;
|
|
--ui-cyan: #4c7f8c;
|
|
--ui-blue: #0053fd;
|
|
--ui-purple: #9e94d5;
|
|
--ui-bg-chrome: color-mix(
|
|
in srgb,
|
|
var(--theme-background-seed) var(--theme-mix-chrome),
|
|
var(--theme-neutral-chrome)
|
|
);
|
|
--ui-bg-sidebar: color-mix(
|
|
in srgb,
|
|
var(--theme-sidebar-seed) var(--theme-mix-sidebar),
|
|
var(--theme-neutral-sidebar)
|
|
);
|
|
--ui-bg-editor: color-mix(in srgb, var(--theme-card-seed) var(--theme-mix-card), var(--theme-neutral-card));
|
|
--ui-bg-elevated: color-mix(
|
|
in srgb,
|
|
var(--theme-elevated-seed) var(--theme-mix-elevated),
|
|
var(--theme-neutral-card)
|
|
);
|
|
--ui-bg-card: color-mix(in srgb, var(--ui-accent) 4%, color-mix(in srgb, var(--ui-base) 4%, transparent));
|
|
--ui-bg-input: #fcfcfc;
|
|
--ui-bg-primary: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-fill-primary-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 10%, transparent)
|
|
);
|
|
--ui-bg-secondary: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-fill-secondary-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 7%, transparent)
|
|
);
|
|
--ui-bg-tertiary: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-fill-tertiary-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 5%, transparent)
|
|
);
|
|
--ui-bg-quaternary: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-fill-quaternary-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 4%, transparent)
|
|
);
|
|
--ui-bg-quinary: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-fill-quinary-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 3%, transparent)
|
|
);
|
|
--ui-row-hover-background: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-row-hover-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 3%, transparent)
|
|
);
|
|
--ui-row-active-background: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-row-active-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 5%, transparent)
|
|
);
|
|
--ui-control-hover-background: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-control-hover-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 4%, transparent)
|
|
);
|
|
--ui-control-active-background: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-control-active-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 5%, transparent)
|
|
);
|
|
--ui-text-primary: color-mix(in srgb, var(--ui-base) 94%, transparent);
|
|
--ui-text-secondary: color-mix(in srgb, var(--ui-base) 74%, transparent);
|
|
--ui-text-tertiary: color-mix(in srgb, var(--ui-base) 54%, transparent);
|
|
--ui-text-quaternary: color-mix(in srgb, var(--ui-base) 36%, transparent);
|
|
--ui-stroke-primary: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-stroke-primary-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 10%, transparent)
|
|
);
|
|
--ui-stroke-secondary: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-stroke-secondary-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 7%, transparent)
|
|
);
|
|
--ui-stroke-tertiary: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-stroke-tertiary-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 5%, transparent)
|
|
);
|
|
--ui-stroke-quaternary: color-mix(
|
|
in srgb,
|
|
var(--ui-accent) var(--theme-stroke-quaternary-accent-mix),
|
|
color-mix(in srgb, var(--ui-base) 3%, transparent)
|
|
);
|
|
--ui-sash-hover-border: color-mix(in srgb, var(--ui-accent) 18%, var(--ui-stroke-tertiary));
|
|
--ui-sash-hover-background: color-mix(in srgb, var(--ui-accent) 6%, transparent);
|
|
--ui-surface-background: var(--ui-bg-editor);
|
|
--ui-sidebar-surface-background: var(--ui-bg-sidebar);
|
|
--ui-chat-surface-background: var(--ui-bg-chrome);
|
|
--ui-editor-surface-background: var(--ui-bg-chrome);
|
|
--ui-chat-bubble-background: color-mix(
|
|
in srgb,
|
|
var(--theme-bubble-seed) var(--theme-mix-bubble),
|
|
var(--theme-neutral-card)
|
|
);
|
|
--ui-chat-bubble-opaque-background: var(--ui-bg-editor);
|
|
--ui-inline-code-background: color-mix(in srgb, #141414 5%, transparent);
|
|
--ui-inline-code-border: color-mix(in srgb, #141414 8%, transparent);
|
|
--ui-inline-code-foreground: color-mix(in srgb, #141414 88%, transparent);
|
|
--ui-selection-background: color-mix(in srgb, #ffd24a 55%, transparent);
|
|
|
|
--dt-background: var(--ui-bg-chrome);
|
|
--dt-foreground: var(--ui-text-primary);
|
|
--dt-card: var(--ui-bg-editor);
|
|
--dt-card-foreground: var(--ui-text-primary);
|
|
--dt-muted: var(--ui-bg-tertiary);
|
|
--dt-muted-foreground: var(--ui-text-tertiary);
|
|
--dt-popover: color-mix(in srgb, var(--ui-bg-elevated) 96%, transparent);
|
|
--dt-popover-foreground: var(--ui-text-primary);
|
|
--dt-primary: var(--theme-primary);
|
|
--dt-primary-foreground: #fcfcfc;
|
|
--dt-secondary: var(--theme-secondary);
|
|
--dt-secondary-foreground: var(--ui-text-secondary);
|
|
--dt-accent: var(--theme-accent-soft);
|
|
--dt-accent-foreground: var(--ui-text-primary);
|
|
--dt-border: var(--ui-stroke-secondary);
|
|
--dt-input: var(--ui-stroke-primary);
|
|
--dt-ring: var(--ui-stroke-primary);
|
|
--dt-midground: var(--theme-midground);
|
|
--dt-composer-ring: var(--ui-base);
|
|
--dt-destructive: #cf2d56;
|
|
--dt-destructive-foreground: #ffffff;
|
|
--dt-sidebar-bg: var(--ui-bg-sidebar);
|
|
--dt-sidebar-border: var(--ui-stroke-secondary);
|
|
--dt-user-bubble: var(--ui-chat-bubble-background);
|
|
--dt-user-bubble-border: var(--ui-stroke-tertiary);
|
|
|
|
--dt-font-sans: 'Segoe WPC', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif;
|
|
--dt-font-mono: 'Cascadia Code', 'JetBrains Mono', 'SF Mono', ui-monospace, Menlo, Consolas, monospace;
|
|
--dt-base-size: 1rem;
|
|
--dt-line-height: 1.5;
|
|
--dt-letter-spacing: 0;
|
|
--dt-spacing-mul: 1;
|
|
|
|
--radius: 0.75rem;
|
|
--radius-scalar: 0.6;
|
|
|
|
/* Space under last message vs overlay composer — driven by the measured composer height (see composer/index.tsx). */
|
|
--thread-last-message-clearance: calc(var(--composer-measured-height) + 2rem);
|
|
|
|
--composer-shell-pad-block-end: 0.625rem;
|
|
--message-text-indent: 0.75rem;
|
|
--conversation-text-font-size: 0.8125rem;
|
|
--conversation-tool-font-size: var(--conversation-text-font-size);
|
|
--conversation-caption-font-size: 0.75rem;
|
|
--conversation-line-height: 1.125rem;
|
|
--conversation-caption-line-height: 1rem;
|
|
--conversation-turn-gap: 0.375rem;
|
|
--sticky-human-top: 0.23rem;
|
|
--file-tree-row-height: 1.375rem;
|
|
|
|
--composer-width: 48.75rem;
|
|
--composer-control-size: 1.75rem;
|
|
--composer-control-primary-size: 1.875rem;
|
|
--composer-control-gap: 0.25rem;
|
|
--composer-row-gap: 0.25rem;
|
|
--composer-ring-strength: 1;
|
|
--composer-surface-pad-x: 0.5rem;
|
|
--composer-surface-pad-y: 0.3125rem;
|
|
--composer-input-min-height: 1.625rem;
|
|
--composer-input-max-height: 9.375rem;
|
|
--composer-input-inline-min-width: 8rem;
|
|
--composer-fallback-height: 2.75rem;
|
|
--composer-measured-height: calc(0.5rem + var(--composer-shell-pad-block-end) + var(--composer-fallback-height));
|
|
--composer-surface-measured-height: var(--composer-fallback-height);
|
|
--thread-viewport-height: max(
|
|
0rem,
|
|
calc(100% - var(--composer-measured-height) + var(--composer-surface-measured-height))
|
|
);
|
|
--vsq: min(0.5vh, 0.5vw);
|
|
--image-preview-max-width: 34rem;
|
|
--image-preview-height: clamp(16.25rem, calc(var(--vsq) * 100), 26.25rem);
|
|
|
|
--sidebar-width: 14.8125rem;
|
|
--chat-min-width: 28rem;
|
|
--titlebar-control-size: 1.25rem;
|
|
--titlebar-control-height: 1.375rem;
|
|
--sidebar-content-inline-padding: 1rem;
|
|
|
|
--sidebar: var(--dt-sidebar-bg);
|
|
--sidebar-foreground: var(--dt-foreground);
|
|
--sidebar-primary: var(--dt-primary);
|
|
--sidebar-primary-foreground: var(--dt-primary-foreground);
|
|
--sidebar-accent: var(--ui-control-active-background);
|
|
--sidebar-accent-foreground: var(--dt-accent-foreground);
|
|
--sidebar-border: var(--dt-sidebar-border);
|
|
--sidebar-ring: var(--dt-ring);
|
|
--sidebar-edge-border: color-mix(in srgb, var(--ui-base) 7.5%, transparent);
|
|
--chrome-action-hover: var(--ui-control-hover-background);
|
|
|
|
--midground: var(--dt-midground);
|
|
--background: var(--dt-background);
|
|
--foreground: var(--dt-foreground);
|
|
|
|
--warm-glow: color-mix(in srgb, var(--ui-warm) 32%, color-mix(in srgb, var(--ui-accent) 6%, transparent));
|
|
/* `--noise-opacity-mul` is set per-mode by `applyTheme()`. */
|
|
--noise-opacity-mul: 1;
|
|
--backdrop-invert-mul: 1;
|
|
}
|
|
|
|
:root.dark {
|
|
/* Per-mode mix knobs — overridden inline by `applyTheme()` per skin. */
|
|
--theme-mix-chrome: 74%;
|
|
--theme-mix-card: 38%;
|
|
--theme-mix-elevated: 46%;
|
|
--theme-mix-bubble: 46%;
|
|
--theme-neutral-chrome: #0d0d0e;
|
|
--theme-neutral-sidebar: #0a0a0b;
|
|
--theme-neutral-card: #161618;
|
|
|
|
/* Dark-only accent palette overrides. */
|
|
--ui-red: #e75e78;
|
|
--ui-green: #55a583;
|
|
--ui-cyan: #6f9ba6;
|
|
|
|
--sidebar-edge-border: color-mix(in srgb, var(--ui-base) 12%, transparent);
|
|
--composer-ring-strength: 1.3;
|
|
--backdrop-invert-mul: 0;
|
|
|
|
--ui-inline-code-background: color-mix(in srgb, #ffffff 7%, transparent);
|
|
--ui-inline-code-border: color-mix(in srgb, #ffffff 10%, transparent);
|
|
--ui-inline-code-foreground: color-mix(in srgb, #ffffff 88%, transparent);
|
|
--ui-selection-background: color-mix(in srgb, #ffd24a 38%, transparent);
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
border-color: var(--dt-border);
|
|
}
|
|
|
|
html,
|
|
body,
|
|
#root {
|
|
height: 100%;
|
|
}
|
|
|
|
html {
|
|
font-size: var(--dt-base-size, 0.875rem);
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
background: var(--ui-chat-surface-background);
|
|
color: var(--dt-foreground);
|
|
font-family: var(--dt-font-sans);
|
|
font-size: 0.8125rem;
|
|
line-height: var(--dt-line-height, 1.55);
|
|
letter-spacing: var(--dt-letter-spacing, 0);
|
|
overflow: hidden;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
button,
|
|
textarea {
|
|
font: inherit;
|
|
}
|
|
|
|
:where(
|
|
a,
|
|
.underline,
|
|
[class~='hover:underline'],
|
|
[class~='focus:underline'],
|
|
[class~='focus-visible:underline'],
|
|
[class~='group-hover:underline'],
|
|
[class~='peer-hover:underline']
|
|
) {
|
|
text-decoration-color: color-mix(in srgb, currentColor 20%, transparent);
|
|
text-underline-offset: 0.25rem;
|
|
}
|
|
|
|
*::selection {
|
|
background: var(--ui-selection-background);
|
|
color: inherit;
|
|
}
|
|
}
|
|
|
|
.dither {
|
|
background: repeating-conic-gradient(currentColor 0% 25%, transparent 0% 50%) 0 0 / 0.125rem 0.125rem;
|
|
}
|
|
|
|
:root:not([style*='--theme-asset-bg:']) .theme-default-filler {
|
|
display: block;
|
|
}
|
|
|
|
:root[style*='--theme-asset-bg:'] .theme-default-filler {
|
|
display: none;
|
|
}
|
|
|
|
@layer utilities {
|
|
[class*='rounded-full'],
|
|
[class*=':rounded-full'] {
|
|
border-radius: calc(var(--radius-scalar) * 9999rem);
|
|
}
|
|
}
|
|
|
|
@keyframes arc-border {
|
|
0% {
|
|
background-position: 15% 15%;
|
|
}
|
|
100% {
|
|
background-position: 75% 75%;
|
|
}
|
|
}
|
|
|
|
.arc-border {
|
|
--arc-c0: color-mix(in srgb, var(--dt-foreground) 0%, transparent);
|
|
--arc-c1: var(--dt-midground);
|
|
--arc-c2: var(--dt-background);
|
|
--arc-angle: 160deg;
|
|
--arc-width: 0.078125rem;
|
|
--arc-inset: -0.125rem;
|
|
--arc-duration: 2.23s;
|
|
|
|
pointer-events: none;
|
|
position: absolute;
|
|
overflow: hidden;
|
|
border-radius: inherit;
|
|
inset: var(--arc-inset);
|
|
padding: var(--arc-width);
|
|
mask:
|
|
linear-gradient(#000 0 0) content-box,
|
|
linear-gradient(#000 0 0);
|
|
-webkit-mask-composite: xor;
|
|
mask-composite: exclude;
|
|
}
|
|
|
|
:root.dark .arc-border {
|
|
--arc-c1: var(--dt-foreground);
|
|
}
|
|
|
|
.arc-border::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
border-radius: inherit;
|
|
background: linear-gradient(
|
|
var(--arc-angle),
|
|
transparent 0%,
|
|
var(--arc-c0) 15%,
|
|
var(--arc-c1) 20%,
|
|
var(--arc-c2) 25%,
|
|
transparent 35%,
|
|
transparent 40%,
|
|
var(--arc-c0) 55%,
|
|
var(--arc-c1) 60%,
|
|
var(--arc-c2) 65%,
|
|
transparent 75%,
|
|
transparent 80%,
|
|
var(--arc-c0) 95%,
|
|
var(--arc-c1) 100%
|
|
);
|
|
background-size: 300% 300%;
|
|
animation: arc-border var(--arc-duration) linear infinite;
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.arc-border::before {
|
|
animation: none;
|
|
}
|
|
}
|
|
|
|
button {
|
|
-webkit-app-region: no-drag;
|
|
}
|
|
|
|
[data-slot='button'] {
|
|
box-shadow: none;
|
|
transition-duration: 100ms;
|
|
}
|
|
|
|
[data-slot='button'][data-variant='outline'],
|
|
[data-slot='button'][data-variant='secondary'] {
|
|
border-color: var(--ui-stroke-secondary);
|
|
background: var(--ui-bg-tertiary);
|
|
color: var(--ui-text-primary);
|
|
}
|
|
|
|
[data-slot='button'][data-variant='ghost'] {
|
|
color: var(--ui-text-secondary);
|
|
}
|
|
|
|
[data-slot='button'][data-variant='outline']:hover,
|
|
[data-slot='button'][data-variant='secondary']:hover,
|
|
[data-slot='button'][data-variant='ghost']:hover {
|
|
background: var(--chrome-action-hover);
|
|
color: var(--ui-text-primary);
|
|
}
|
|
|
|
[data-slot='dropdown-menu-content'],
|
|
[data-slot='select-content'],
|
|
[data-slot='dialog-content'] {
|
|
border-color: var(--ui-stroke-secondary);
|
|
background: color-mix(in srgb, var(--ui-bg-elevated) 96%, transparent);
|
|
box-shadow: var(--shadow-md);
|
|
backdrop-filter: blur(0.75rem) saturate(1.08);
|
|
-webkit-backdrop-filter: blur(0.75rem) saturate(1.08);
|
|
}
|
|
|
|
[data-slot='dropdown-menu-item']:focus,
|
|
[data-slot='dropdown-menu-checkbox-item']:focus,
|
|
[data-slot='dropdown-menu-radio-item']:focus {
|
|
background: var(--ui-bg-tertiary);
|
|
color: var(--ui-text-primary);
|
|
}
|
|
|
|
input,
|
|
textarea,
|
|
[contenteditable]:not([contenteditable='false']),
|
|
[data-slot='aui_user-message-root'],
|
|
[data-slot='aui_assistant-message-content'],
|
|
[data-selectable-text='true'],
|
|
[data-selectable-text='true'] * {
|
|
-webkit-user-select: text;
|
|
user-select: text;
|
|
}
|
|
|
|
button,
|
|
[role='button'] {
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
}
|
|
|
|
img,
|
|
picture,
|
|
video,
|
|
canvas,
|
|
svg {
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
}
|
|
|
|
img,
|
|
video,
|
|
canvas {
|
|
-webkit-user-drag: none;
|
|
}
|
|
|
|
/* Shared input chrome — mirrors composer hover/focus FX. Unlayered to beat Tailwind utilities. */
|
|
.desktop-input-chrome {
|
|
--ring-pct: 18%;
|
|
--ring-fall: var(--dt-input);
|
|
background: color-mix(in srgb, var(--dt-card) 68%, transparent);
|
|
border-color: color-mix(
|
|
in srgb,
|
|
var(--dt-composer-ring) calc(var(--ring-pct) * var(--composer-ring-strength)),
|
|
var(--ring-fall)
|
|
);
|
|
box-shadow: var(--shadow-composer);
|
|
transition:
|
|
background-color 200ms ease-out,
|
|
border-color 200ms ease-out,
|
|
box-shadow 200ms ease-out;
|
|
}
|
|
|
|
.desktop-input-chrome:hover {
|
|
--ring-pct: 30%;
|
|
background: color-mix(in srgb, var(--dt-card) 86%, transparent);
|
|
}
|
|
|
|
.desktop-input-chrome:focus {
|
|
--ring-pct: 45%;
|
|
--ring-fall: transparent;
|
|
background: var(--dt-card);
|
|
box-shadow: var(--shadow-composer-focus);
|
|
outline: none;
|
|
}
|
|
|
|
.desktop-input-chrome[aria-invalid='true'] {
|
|
border-color: var(--dt-destructive);
|
|
}
|
|
|
|
.desktop-input-chrome[aria-invalid='true']:focus {
|
|
box-shadow:
|
|
0 0 0 0.125rem color-mix(in srgb, var(--dt-destructive) 18%, transparent),
|
|
0 0 0 0.0625rem color-mix(in srgb, var(--dt-destructive) 34%, transparent),
|
|
0 0.1875rem 0.625rem color-mix(in srgb, var(--dt-destructive) 12%, transparent);
|
|
}
|
|
|
|
@layer components {
|
|
.scrollbar-dt,
|
|
.scrollbar-dt * {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: color-mix(in srgb, var(--dt-midground) 18%, transparent) transparent;
|
|
}
|
|
|
|
.scrollbar-dt::-webkit-scrollbar,
|
|
.scrollbar-dt *::-webkit-scrollbar {
|
|
width: 0.5rem;
|
|
height: 0.5rem;
|
|
}
|
|
|
|
.scrollbar-dt::-webkit-scrollbar-track,
|
|
.scrollbar-dt::-webkit-scrollbar-corner,
|
|
.scrollbar-dt *::-webkit-scrollbar-track,
|
|
.scrollbar-dt *::-webkit-scrollbar-corner {
|
|
background: transparent;
|
|
}
|
|
|
|
.scrollbar-dt::-webkit-scrollbar-thumb,
|
|
.scrollbar-dt *::-webkit-scrollbar-thumb {
|
|
background: color-mix(in srgb, var(--dt-midground) 18%, transparent);
|
|
border-radius: 9999rem;
|
|
border: 0.125rem solid transparent;
|
|
background-clip: padding-box;
|
|
}
|
|
|
|
.scrollbar-dt::-webkit-scrollbar-thumb:hover,
|
|
.scrollbar-dt *::-webkit-scrollbar-thumb:hover {
|
|
background: color-mix(in srgb, var(--dt-midground) 40%, transparent);
|
|
background-clip: padding-box;
|
|
}
|
|
|
|
.scrollbar-dt::-webkit-scrollbar-button,
|
|
.scrollbar-dt *::-webkit-scrollbar-button {
|
|
display: none;
|
|
}
|
|
|
|
/* Variant for portaled overlays (Radix DropdownMenu, Popover, etc.) that
|
|
render under document.body, outside the `.scrollbar-dt` scope on
|
|
#root. Same visual treatment, applied directly to the overlay
|
|
container so its (and only its) internal scrollbar is themed. */
|
|
.dt-portal-scrollbar {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: color-mix(in srgb, var(--dt-midground) 28%, transparent) transparent;
|
|
}
|
|
|
|
.dt-portal-scrollbar::-webkit-scrollbar {
|
|
width: 0.375rem;
|
|
height: 0.375rem;
|
|
}
|
|
|
|
.dt-portal-scrollbar::-webkit-scrollbar-track,
|
|
.dt-portal-scrollbar::-webkit-scrollbar-corner {
|
|
background: transparent;
|
|
}
|
|
|
|
.dt-portal-scrollbar::-webkit-scrollbar-thumb {
|
|
background: color-mix(in srgb, var(--dt-midground) 28%, transparent);
|
|
border-radius: 9999rem;
|
|
border: 0.0625rem solid transparent;
|
|
background-clip: padding-box;
|
|
}
|
|
|
|
.dt-portal-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
background: color-mix(in srgb, var(--dt-midground) 50%, transparent);
|
|
background-clip: padding-box;
|
|
}
|
|
|
|
.dt-portal-scrollbar::-webkit-scrollbar-button {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
/* Bottom clearance lives on [data-slot='aui_composer-clearance'] —
|
|
virtualized items unmount, so :nth-last-child can't fire reliably. */
|
|
|
|
[data-slot='aui_assistant-message-content'] {
|
|
padding-left: var(--message-text-indent);
|
|
font-size: var(--conversation-text-font-size);
|
|
line-height: 1.5;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-root'] {
|
|
width: 100%;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] .aui-md,
|
|
[data-slot='aui_assistant-message-content'] .aui-md :where(p, li, blockquote, table, pre) {
|
|
font-size: inherit;
|
|
}
|
|
|
|
/* Streamed prose hangs slightly indented from the tool/todo column so the
|
|
reading column reads as a "reply" within the conversation gutter. Tools,
|
|
todos, and thinking blocks keep the existing --message-text-indent so they
|
|
remain flush with the user message text above them. */
|
|
[data-slot='aui_assistant-message-content'] > .aui-md {
|
|
padding-inline-start: var(--md-text-indent, 0.5rem);
|
|
}
|
|
|
|
[data-slot='aui_user-message-root'] {
|
|
top: var(--sticky-human-top);
|
|
}
|
|
|
|
[data-slot='aui_user-message-root'],
|
|
[data-slot='aui_edit-composer-root'] {
|
|
font-size: var(--conversation-text-font-size);
|
|
}
|
|
|
|
/* Sticky human bubbles clamp to ~2 lines with a soft bottom fade so a long
|
|
prompt doesn't dominate the viewport while you read the response stuck
|
|
beneath it. The clamp lifts on hover / focus (clicking the bubble opens the
|
|
edit composer, which already shows the full text). --human-msg-full is the
|
|
measured content height (set in UserMessage) so expand/collapse animates to
|
|
the real height instead of overshooting the cap. */
|
|
.sticky-human-clamp {
|
|
max-height: calc(2 * var(--dt-line-height) * var(--conversation-text-font-size) + 0.15rem);
|
|
overflow: hidden;
|
|
transition: max-height 0.08s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
|
|
.sticky-human-clamp[data-clamped='true'] {
|
|
-webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent);
|
|
mask-image: linear-gradient(to bottom, #000 55%, transparent);
|
|
}
|
|
|
|
.composer-human-message:hover .sticky-human-clamp,
|
|
.composer-human-message:focus-within .sticky-human-clamp {
|
|
max-height: min(var(--human-msg-full, 24rem), 24rem);
|
|
overflow-y: auto;
|
|
-webkit-mask-image: none;
|
|
mask-image: none;
|
|
}
|
|
|
|
/* The thread renders items in natural document flow (padding spacers, not
|
|
transforms) and @tanstack/react-virtual already adjusts scrollTop itself
|
|
when an off-screen turn is measured and its real height differs from the
|
|
220px estimate. The browser's native scroll anchoring (overflow-anchor:
|
|
auto) would adjust scrollTop for that SAME size delta, so the two
|
|
double-correct and the view lurches — most visibly on Windows mouse wheels,
|
|
whose coarse notches mount/measure several under-estimated turns per tick.
|
|
Opt out of native anchoring so only the virtualizer compensates. */
|
|
[data-slot='aui_thread-viewport'] {
|
|
overflow-anchor: none;
|
|
}
|
|
|
|
[data-slot='aui_thread-content'] {
|
|
max-width: var(--composer-width);
|
|
padding-inline: 1.5rem;
|
|
}
|
|
|
|
[data-slot='aui_intro'] {
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding-bottom: var(--composer-measured-height);
|
|
text-align: center;
|
|
}
|
|
|
|
[data-slot='aui_intro'] > div {
|
|
max-width: min(var(--composer-width), 82vw);
|
|
}
|
|
|
|
[data-slot='aui_intro'] p:last-child {
|
|
max-width: 34rem;
|
|
margin-inline: auto;
|
|
color: var(--ui-text-tertiary);
|
|
font-size: 0.875rem;
|
|
line-height: 1.45;
|
|
}
|
|
|
|
.fit-text {
|
|
display: flex;
|
|
font-size: var(--fit-text-min, 1rem);
|
|
container-type: inline-size;
|
|
--captured-length: initial;
|
|
--support-sentinel: var(--captured-length, 9999px);
|
|
}
|
|
|
|
.fit-text > [aria-hidden='true'] {
|
|
visibility: hidden;
|
|
}
|
|
|
|
.fit-text > :not([aria-hidden='true']) {
|
|
flex-grow: 1;
|
|
container-type: inline-size;
|
|
--captured-length: 100cqi;
|
|
--available-space: var(--captured-length);
|
|
}
|
|
|
|
.fit-text > :not([aria-hidden='true']) > * {
|
|
display: block;
|
|
inline-size: var(--available-space);
|
|
line-height: var(--fit-text-line-height, 1);
|
|
--support-sentinel: inherit;
|
|
--captured-length: 100cqi;
|
|
--ratio: tan(atan2(var(--available-space), var(--available-space) - var(--captured-length)));
|
|
--font-size: clamp(
|
|
var(--fit-text-min, 1em),
|
|
1em * var(--ratio),
|
|
var(--fit-text-max, infinity * 1px) - var(--support-sentinel)
|
|
);
|
|
font-size: var(--font-size);
|
|
}
|
|
|
|
@container (inline-size > 0) {
|
|
.fit-text > :not([aria-hidden='true']) > * {
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
|
|
@property --captured-length {
|
|
syntax: '<length>';
|
|
initial-value: 0px;
|
|
inherits: true;
|
|
}
|
|
|
|
@property --captured-length2 {
|
|
syntax: '<length>';
|
|
initial-value: 0px;
|
|
inherits: true;
|
|
}
|
|
|
|
[data-slot='composer-root'] {
|
|
width: min(var(--composer-width), calc(100% - 2rem));
|
|
padding-bottom: var(--composer-shell-pad-block-end);
|
|
}
|
|
|
|
[data-slot='composer-root'] > .pointer-events-none {
|
|
background: linear-gradient(
|
|
to bottom,
|
|
transparent,
|
|
color-mix(in srgb, var(--ui-chat-surface-background) 88%, transparent)
|
|
) !important;
|
|
}
|
|
|
|
[data-slot='composer-surface'] {
|
|
border-color: var(--ui-stroke-secondary) !important;
|
|
box-shadow: var(--shadow-composer) !important;
|
|
}
|
|
|
|
[data-slot='composer-fade'] {
|
|
min-height: 2.375rem;
|
|
}
|
|
|
|
[data-slot='composer-rich-input'] {
|
|
color: var(--ui-text-primary);
|
|
font-size: 0.8125rem;
|
|
}
|
|
|
|
[data-slot='composer-rich-input']:empty::before {
|
|
color: var(--ui-text-tertiary) !important;
|
|
}
|
|
|
|
[data-slot='composer-root']:focus-within [data-slot='composer-surface'] > [aria-hidden='true'] {
|
|
background: var(--ui-chat-bubble-background) !important;
|
|
}
|
|
|
|
/* Tool/thinking blocks now live at message-text alignment (no leading
|
|
chevron column to escape into), so their headers and bodies share a
|
|
common left edge with the model's text. */
|
|
[data-slot='aui_assistant-message-content'] > [data-slot='tool-block'],
|
|
[data-slot='aui_assistant-message-content'] > [data-slot='aui_thinking-disclosure'] {
|
|
width: 100%;
|
|
max-width: 100%;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] .aui-md [data-streamdown='code-block'] code {
|
|
max-width: none;
|
|
font-family: inherit;
|
|
font-size: inherit;
|
|
padding: 0;
|
|
border-radius: 0;
|
|
background: transparent;
|
|
color: inherit;
|
|
overflow-x: visible;
|
|
overflow-wrap: inherit;
|
|
vertical-align: baseline;
|
|
word-break: inherit;
|
|
white-space: inherit;
|
|
}
|
|
|
|
/* Streamdown's adapter wraps code fences in a `data-streamdown="code-block"`
|
|
container with its own card chrome. We render our own <CodeCard>, so this
|
|
strips the upstream chrome down to a layout-only passthrough. */
|
|
[data-slot='aui_assistant-message-content'] .aui-md [data-streamdown='code-block'] {
|
|
contain: none;
|
|
overflow: visible;
|
|
margin-block: 0.375rem !important;
|
|
padding: 0 !important;
|
|
gap: 0 !important;
|
|
border: 0 !important;
|
|
border-radius: 0 !important;
|
|
background: transparent !important;
|
|
color: inherit;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] .aui-md [data-streamdown='code-block']:has(.aui-prose-fence) {
|
|
margin-block: 0 !important;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] .aui-md [data-slot='code-card'] {
|
|
position: relative;
|
|
transition:
|
|
border-color 180ms ease-out,
|
|
box-shadow 180ms ease-out,
|
|
background-color 180ms ease-out;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] .aui-md [data-slot='code-card'][data-streaming='true'] {
|
|
animation:
|
|
code-card-stream-enter 180ms cubic-bezier(0.16, 1, 0.3, 1) both,
|
|
code-card-stream-glow 1.8s ease-in-out 180ms infinite alternate;
|
|
border-color: color-mix(in srgb, var(--dt-ring) 24%, var(--ui-stroke-tertiary));
|
|
box-shadow:
|
|
0 0 0 0.0625rem color-mix(in srgb, var(--dt-ring) 10%, transparent),
|
|
0 0.625rem 1.75rem color-mix(in srgb, var(--dt-ring) 8%, transparent);
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content']
|
|
.aui-md
|
|
[data-slot='code-card'][data-streaming='true']
|
|
[data-slot='code-card-body'] {
|
|
-webkit-mask-image: linear-gradient(to bottom, black 0%, black calc(100% - 1.5rem), rgb(0 0 0 / 64%) 100%);
|
|
mask-image: linear-gradient(to bottom, black 0%, black calc(100% - 1.5rem), rgb(0 0 0 / 64%) 100%);
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] .aui-md :not(pre) > code {
|
|
border: 0.0625rem solid var(--ui-inline-code-border);
|
|
background: var(--ui-inline-code-background);
|
|
color: var(--ui-inline-code-foreground);
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] .aui-md :where(.aui-shiki, .aui-shiki > pre) {
|
|
margin: 0 !important;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] .aui-md .aui-md-table {
|
|
border-spacing: 0;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] .aui-md .aui-md-table > table,
|
|
[data-slot='aui_assistant-message-content'] .aui-md .aui-md-table thead,
|
|
[data-slot='aui_assistant-message-content'] .aui-md .aui-md-table tbody,
|
|
[data-slot='aui_assistant-message-content'] .aui-md .aui-md-table tr,
|
|
[data-slot='aui_assistant-message-content'] .aui-md .aui-md-table th,
|
|
[data-slot='aui_assistant-message-content'] .aui-md .aui-md-table td {
|
|
margin: 0 !important;
|
|
margin-block-start: 0 !important;
|
|
margin-block-end: 0 !important;
|
|
}
|
|
|
|
/* Tool / thinking blocks are scaffolding around the model's reply, so we
|
|
keep them transparent and fade them slightly. The reading column (prose)
|
|
stays at full strength; scaffolding lifts back to full opacity on
|
|
hover/focus so it stays legible when the user actually wants to read it. */
|
|
[data-slot='tool-block'],
|
|
[data-slot='aui_thinking-disclosure'] {
|
|
background: transparent !important;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] > :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure']) {
|
|
opacity: 0.67;
|
|
transition: opacity 120ms ease-out;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content']
|
|
> :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure']):is(:hover, :focus-within) {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Conversation block rhythm. Consecutive tool calls stay tight so a step
|
|
sequence reads as one action group; the gap between any scaffolding
|
|
block and adjacent prose bumps up so the model's reply visually
|
|
separates from its scaffolding. */
|
|
[data-slot='tool-block'] + [data-slot='tool-block'] {
|
|
margin-top: 0.375rem;
|
|
}
|
|
|
|
[data-slot='tool-block']:has(> :nth-child(2)) + [data-slot='tool-block'] {
|
|
margin-top: 0.625rem;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content']
|
|
:is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure'])
|
|
+ .aui-md,
|
|
[data-slot='aui_assistant-message-content']
|
|
.aui-md
|
|
+ :is([data-slot='tool-block'], [data-slot='aui_thinking-disclosure']) {
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] [data-slot='aui_thinking-disclosure'] + [data-slot='tool-block'],
|
|
[data-slot='aui_assistant-message-content'] [data-slot='tool-block'] + [data-slot='aui_thinking-disclosure'] {
|
|
margin-top: 0.75rem;
|
|
}
|
|
|
|
[data-slot='aui_assistant-message-content'] > [data-slot='tool-block']:first-child {
|
|
margin-top: 0;
|
|
}
|
|
|
|
/* Message action bars — flat icon hits with default dim; only the hovered/focused control is full-strength. */
|
|
[data-slot='aui_msg-actions'] button {
|
|
border: 0;
|
|
border-radius: 0;
|
|
background: transparent;
|
|
box-shadow: none;
|
|
padding: 0;
|
|
gap: 0;
|
|
height: auto;
|
|
width: auto;
|
|
min-height: 0;
|
|
min-width: 0;
|
|
flex-shrink: 0;
|
|
cursor: pointer;
|
|
color: var(--color-muted-foreground);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
[data-slot='aui_msg-actions'] button:disabled {
|
|
cursor: default;
|
|
}
|
|
|
|
[data-slot='aui_msg-actions'] button:hover {
|
|
background: transparent;
|
|
color: var(--color-foreground);
|
|
opacity: 1;
|
|
}
|
|
|
|
[data-slot='aui_msg-actions'] button:active {
|
|
background: transparent;
|
|
}
|
|
|
|
[data-slot='aui_msg-actions'] button:focus-visible {
|
|
opacity: 1;
|
|
}
|
|
|
|
[data-slot='aui_msg-actions'] button svg {
|
|
width: 0.875rem;
|
|
height: 0.875rem;
|
|
}
|
|
|
|
/* Live thinking preview window. Pairs with the ResizeObserver in
|
|
ThinkingDisclosure that pins scrollTop to the bottom — older lines fade
|
|
into the top mask while the latest tokens settle in below. */
|
|
.thinking-preview {
|
|
-webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 28%, black 100%);
|
|
mask-image: linear-gradient(to bottom, transparent 0%, black 28%, black 100%);
|
|
}
|
|
|
|
@keyframes code-card-stream-enter {
|
|
from {
|
|
opacity: 0.74;
|
|
transform: translateY(0.375rem);
|
|
}
|
|
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes code-card-stream-glow {
|
|
from {
|
|
border-color: color-mix(in srgb, var(--dt-ring) 18%, var(--ui-stroke-tertiary));
|
|
box-shadow:
|
|
0 0 0 0.0625rem color-mix(in srgb, var(--dt-ring) 6%, transparent),
|
|
0 0.5rem 1.5rem color-mix(in srgb, var(--dt-ring) 5%, transparent);
|
|
}
|
|
|
|
to {
|
|
border-color: color-mix(in srgb, var(--dt-ring) 32%, var(--ui-stroke-tertiary));
|
|
box-shadow:
|
|
0 0 0 0.0625rem color-mix(in srgb, var(--dt-ring) 12%, transparent),
|
|
0 0.75rem 2rem color-mix(in srgb, var(--dt-ring) 10%, transparent);
|
|
}
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
[data-slot='aui_assistant-message-content'] .aui-md [data-slot='code-card'][data-streaming='true'] {
|
|
animation: none;
|
|
}
|
|
}
|