chore: uptick
This commit is contained in:
@@ -2,8 +2,25 @@ import type { Unstable_TriggerAdapter } from '@assistant-ui/core'
|
||||
import { ComposerPrimitive } from '@assistant-ui/react'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
export const COMPLETION_DRAWER_CLASS = 'composer-completion-drawer'
|
||||
export const COMPLETION_DRAWER_ROW_CLASS = 'composer-completion-row'
|
||||
export const COMPLETION_DRAWER_CLASS = [
|
||||
'absolute inset-x-0 bottom-[calc(100%-0.5rem)] z-50',
|
||||
'max-h-[min(23rem,calc(100vh-8rem))] overflow-y-auto overscroll-contain',
|
||||
'rounded-t-(--composer-active-radius) border border-b-0',
|
||||
'border-[color-mix(in_srgb,var(--dt-ring)_45%,transparent)]',
|
||||
'bg-[color-mix(in_srgb,var(--dt-popover)_96%,transparent)]',
|
||||
'px-1.5 pb-3 pt-1.5 text-popover-foreground',
|
||||
'backdrop-blur-[0.75rem] backdrop-saturate-[1.1]',
|
||||
'[-webkit-backdrop-filter:blur(0.75rem)_saturate(1.1)]',
|
||||
'data-[state=open]:-mb-2',
|
||||
'data-[state=open]:shadow-[0_-0.0625rem_0_0.0625rem_color-mix(in_srgb,var(--dt-ring)_35%,transparent),0_-1rem_2.25rem_-1.75rem_color-mix(in_srgb,var(--dt-foreground)_34%,transparent),0_-0.3125rem_0.875rem_-0.6875rem_color-mix(in_srgb,var(--dt-foreground)_22%,transparent)]'
|
||||
].join(' ')
|
||||
|
||||
export const COMPLETION_DRAWER_ROW_CLASS = [
|
||||
'flex w-full min-w-0 items-baseline gap-2 rounded-md px-2.5 py-1',
|
||||
'text-left text-xs transition-colors',
|
||||
'hover:bg-[color-mix(in_srgb,var(--dt-accent)_70%,transparent)]',
|
||||
'data-[highlighted]:bg-[color-mix(in_srgb,var(--dt-accent)_70%,transparent)]'
|
||||
].join(' ')
|
||||
|
||||
export function ComposerCompletionDrawer({
|
||||
adapter,
|
||||
|
||||
@@ -82,7 +82,7 @@ export function useVoiceConversation({
|
||||
return
|
||||
}
|
||||
|
||||
speechBufferRef.current = `${speechBufferRef.current} ${text}`.trim()
|
||||
speechBufferRef.current = `${speechBufferRef.current}${text}`
|
||||
}
|
||||
|
||||
const takeSpeechChunk = (force = false): string | null => {
|
||||
|
||||
@@ -3,6 +3,8 @@ import { useStore } from '@nanostores/react'
|
||||
import LiquidGlass from 'liquid-glass-react'
|
||||
import { type ClipboardEvent, type CSSProperties, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import './liquid-glass-overrides.css'
|
||||
|
||||
import { hermesDirectiveFormatter } from '@/components/assistant-ui/directive-text'
|
||||
import { useMediaQuery } from '@/hooks/use-media-query'
|
||||
import { chatMessageText } from '@/lib/chat-messages'
|
||||
@@ -27,13 +29,20 @@ import type { ChatBarProps } from './types'
|
||||
import { UrlDialog } from './url-dialog'
|
||||
import { VoiceActivity, VoicePlaybackActivity } from './voice-activity'
|
||||
|
||||
const SHELL =
|
||||
'absolute bottom-0 left-1/2 z-30 w-[min(calc(100%_-_1rem),clamp(26rem,61.8%,56rem))] max-w-full -translate-x-1/2'
|
||||
const COMPOSER_SHELL_CLASS =
|
||||
'group/composer absolute bottom-0 left-1/2 z-30 w-[min(calc(100%-1rem),clamp(26rem,61.8%,56rem))] max-w-full -translate-x-1/2 pt-2 pb-[var(--composer-shell-pad-block-end)]'
|
||||
|
||||
const COMPOSER_BACKDROP_STYLE = {
|
||||
backdropFilter: 'blur(0.75rem) saturate(1.12)',
|
||||
WebkitBackdropFilter: 'blur(0.75rem) saturate(1.12)'
|
||||
} satisfies CSSProperties
|
||||
const COMPOSER_FROST_CLASS = cn(
|
||||
'pointer-events-none absolute inset-0 -z-10 rounded-(--composer-active-radius)',
|
||||
'bg-[color-mix(in_srgb,var(--dt-card)_72%,transparent)]',
|
||||
'backdrop-blur-[0.75rem] backdrop-saturate-[1.12]',
|
||||
'[-webkit-backdrop-filter:blur(0.75rem)_saturate(1.12)]',
|
||||
'transition-[background-color,backdrop-filter,-webkit-backdrop-filter] duration-150 ease-out',
|
||||
'group-data-[thread-scrolled-up]/composer:bg-[color-mix(in_srgb,var(--dt-card)_48%,transparent)]',
|
||||
'group-focus-within/composer:bg-[var(--dt-card)]',
|
||||
'group-focus-within/composer:[backdrop-filter:none]',
|
||||
'group-focus-within/composer:[-webkit-backdrop-filter:none]'
|
||||
)
|
||||
|
||||
export function ChatBar({
|
||||
busy,
|
||||
@@ -344,18 +353,20 @@ export function ChatBar({
|
||||
<>
|
||||
<ComposerPrimitive.Unstable_TriggerPopoverRoot>
|
||||
<ComposerPrimitive.Root
|
||||
className={cn(SHELL, 'group/composer pb-(--composer-shell-pad-block-end) pt-2')}
|
||||
className={COMPOSER_SHELL_CLASS}
|
||||
data-slot="composer-root"
|
||||
style={
|
||||
{
|
||||
'--composer-active-radius': `${glassTweaks.liquid.cornerRadius}px`
|
||||
} as CSSProperties
|
||||
}
|
||||
data-thread-scrolled-up={scrolledUp ? '' : undefined}
|
||||
onSubmit={e => {
|
||||
e.preventDefault()
|
||||
submitDraft()
|
||||
}}
|
||||
ref={composerRef}
|
||||
style={
|
||||
{
|
||||
'--composer-active-radius': `${glassTweaks.liquid.cornerRadius}px`,
|
||||
'--composer-glass-radius': `${glassTweaks.liquid.cornerRadius}px`
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
{showHelpHint && <HelpHint />}
|
||||
<DirectivePopover
|
||||
@@ -365,26 +376,22 @@ export function ChatBar({
|
||||
/>
|
||||
<SlashPopover adapter={slash.adapter} loading={slash.loading} />
|
||||
<div
|
||||
className="pointer-events-none absolute inset-x-0 bottom-0 top-0"
|
||||
className="pointer-events-none absolute inset-0"
|
||||
style={{ background: glassTweaks.fadeBackground }}
|
||||
/>
|
||||
<div className="relative w-full">
|
||||
<div
|
||||
className={cn(
|
||||
'composer-liquid-shell-wrap absolute inset-0 transition-opacity duration-200 ease-out',
|
||||
'composer-liquid-shell-wrap absolute inset-0 isolate overflow-hidden rounded-(--composer-glass-radius,20px) transition-opacity duration-200 ease-out',
|
||||
'group-has-data-[state=open]/composer:rounded-t-none',
|
||||
scrolledUp
|
||||
? 'opacity-70 group-hover/composer:opacity-100 group-focus-within/composer:opacity-100'
|
||||
? 'opacity-30 group-hover/composer:opacity-100 group-focus-within/composer:opacity-100'
|
||||
: 'opacity-100'
|
||||
)}
|
||||
data-glass-frame="true"
|
||||
data-show-library-rims={glassTweaks.showLibraryRims ? 'true' : undefined}
|
||||
data-slot="composer-liquid-shell-wrap"
|
||||
ref={glassShellRef}
|
||||
style={
|
||||
{
|
||||
'--composer-active-radius': `${glassTweaks.liquid.cornerRadius}px`,
|
||||
'--composer-glass-radius': `${glassTweaks.liquid.cornerRadius}px`
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
<LiquidGlass
|
||||
aberrationIntensity={glassTweaks.liquid.aberrationIntensity}
|
||||
@@ -398,38 +405,30 @@ export function ChatBar({
|
||||
mouseContainer={composerRef}
|
||||
padding="0"
|
||||
saturation={glassTweaks.liquid.saturation}
|
||||
style={{ position: 'absolute', top: '0', left: '0', width: '100%', height: '100%' }}
|
||||
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}
|
||||
>
|
||||
<span className="block h-full w-full" />
|
||||
</LiquidGlass>
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'relative z-4 isolate overflow-hidden border border-input/70 shadow-composer transition-[border-color,box-shadow] duration-200 ease-out group-focus-within/composer:border-ring/35 group-focus-within/composer:shadow-composer-focus',
|
||||
scrolledUp ? 'border-input/48' : 'border-input/70'
|
||||
'relative z-4 isolate overflow-hidden rounded-(--composer-active-radius) border border-input/70 shadow-composer transition-[border-color,box-shadow] duration-200 ease-out',
|
||||
'group-focus-within/composer:border-ring/35 group-focus-within/composer:shadow-composer-focus',
|
||||
'group-has-data-[state=open]/composer:rounded-t-none group-has-data-[state=open]/composer:border-t-transparent',
|
||||
'group-has-data-[state=open]/composer:shadow-[0_0.0625rem_0_0.0625rem_color-mix(in_srgb,var(--dt-ring)_35%,transparent),0_0.5rem_1.5rem_color-mix(in_srgb,var(--shadow-ink)_6%,transparent)]'
|
||||
)}
|
||||
data-slot="composer-surface"
|
||||
style={
|
||||
{
|
||||
borderRadius: `${glassTweaks.liquid.cornerRadius}px`,
|
||||
'--composer-active-radius': `${glassTweaks.liquid.cornerRadius}px`
|
||||
} as CSSProperties
|
||||
}
|
||||
>
|
||||
<div aria-hidden className={COMPOSER_FROST_CLASS} />
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute inset-0 -z-10"
|
||||
style={
|
||||
{
|
||||
...COMPOSER_BACKDROP_STYLE,
|
||||
borderRadius: `${glassTweaks.liquid.cornerRadius}px`,
|
||||
backgroundColor: scrolledUp
|
||||
? 'color-mix(in srgb, var(--dt-card) 48%, transparent)'
|
||||
: 'color-mix(in srgb, var(--dt-card) 72%, transparent)'
|
||||
} satisfies CSSProperties
|
||||
}
|
||||
/>
|
||||
<div className="relative z-1 flex min-h-0 w-full flex-col gap-1.5 px-2 py-1.5">
|
||||
className={cn(
|
||||
'relative z-1 flex min-h-0 w-full flex-col gap-1.5 px-2 py-1.5 transition-opacity duration-200 ease-out',
|
||||
scrolledUp
|
||||
? 'opacity-30 group-hover/composer:opacity-100 group-focus-within/composer:opacity-100'
|
||||
: 'opacity-100'
|
||||
)}
|
||||
data-slot="composer-fade"
|
||||
>
|
||||
<VoiceActivity state={voiceActivityState} />
|
||||
<VoicePlaybackActivity />
|
||||
{attachments.length > 0 && (
|
||||
@@ -470,19 +469,13 @@ export function ChatBar({
|
||||
|
||||
export function ChatBarFallback() {
|
||||
return (
|
||||
<div className={cn(SHELL, 'bg-linear-to-b from-transparent to-background/55 pb-(--composer-shell-pad-block-end) pt-2')}>
|
||||
<div className="relative isolate h-11 w-full overflow-hidden rounded-[1.25rem] border border-input/70 shadow-composer">
|
||||
<div
|
||||
aria-hidden
|
||||
className="pointer-events-none absolute inset-0"
|
||||
style={
|
||||
{
|
||||
...COMPOSER_BACKDROP_STYLE,
|
||||
borderRadius: '1.25rem',
|
||||
backgroundColor: 'color-mix(in srgb, var(--dt-card) 72%, transparent)'
|
||||
} satisfies CSSProperties
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className={cn(COMPOSER_SHELL_CLASS, 'bg-linear-to-b from-transparent to-background/55')}
|
||||
data-slot="composer-root"
|
||||
style={{ '--composer-active-radius': '1.25rem' } as CSSProperties}
|
||||
>
|
||||
<div className="relative isolate h-11 w-full overflow-hidden rounded-(--composer-active-radius) border border-input/70 shadow-composer">
|
||||
<div aria-hidden className={COMPOSER_FROST_CLASS} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/* liquid-glass-react emits helper nodes that ignore local utility classes. Keep
|
||||
these overrides scoped by class so the rest of app styling stays utility-first. */
|
||||
.composer-liquid-shell-wrap > div:not(.composer-liquid-shell) {
|
||||
position: absolute !important;
|
||||
inset: 0 !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: none !important;
|
||||
margin: 0 !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.composer-liquid-shell-wrap:not([data-show-library-rims='true']) > span {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell-wrap[data-show-library-rims='true'] > span {
|
||||
position: absolute !important;
|
||||
inset: 0 !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: none !important;
|
||||
margin: 0 !important;
|
||||
box-sizing: border-box;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell {
|
||||
z-index: 1;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
transform: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell > svg {
|
||||
position: absolute !important;
|
||||
inset: 0 !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell > .glass,
|
||||
.composer-liquid-shell > :not(svg):not(.glass) {
|
||||
position: absolute !important;
|
||||
inset: 0 !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell > .glass {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
padding: 0 !important;
|
||||
border-radius: var(--composer-glass-radius, 20px) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell > .glass > .glass__warp {
|
||||
border-radius: var(--composer-glass-radius, 20px) !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell > .glass > div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font: inherit !important;
|
||||
text-shadow: none !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
@@ -13,10 +13,27 @@ type BrowserAudioContext = typeof AudioContext
|
||||
|
||||
interface ElementAnalyser {
|
||||
analyser: AnalyserNode
|
||||
context: AudioContext
|
||||
}
|
||||
|
||||
const elementAnalysers = new WeakMap<HTMLAudioElement, ElementAnalyser>()
|
||||
let playbackAudioContext: AudioContext | null = null
|
||||
|
||||
function getPlaybackAudioContext(): AudioContext | null {
|
||||
if (playbackAudioContext && playbackAudioContext.state !== 'closed') {
|
||||
return playbackAudioContext
|
||||
}
|
||||
|
||||
const audioWindow = window as Window & { webkitAudioContext?: BrowserAudioContext }
|
||||
const AudioContextCtor = window.AudioContext || audioWindow.webkitAudioContext
|
||||
|
||||
if (!AudioContextCtor) {
|
||||
return null
|
||||
}
|
||||
|
||||
playbackAudioContext = new AudioContextCtor()
|
||||
|
||||
return playbackAudioContext
|
||||
}
|
||||
|
||||
function formatElapsed(seconds: number) {
|
||||
const safeSeconds = Math.max(0, Math.floor(seconds))
|
||||
@@ -51,17 +68,15 @@ function VoiceLevelBars({ level, active }: { active: boolean; level: number }) {
|
||||
}
|
||||
|
||||
function getElementAnalyser(audioElement: HTMLAudioElement): ElementAnalyser | null {
|
||||
const audioWindow = window as Window & { webkitAudioContext?: BrowserAudioContext }
|
||||
const AudioContextCtor = window.AudioContext || audioWindow.webkitAudioContext
|
||||
|
||||
if (!AudioContextCtor) {
|
||||
return null
|
||||
}
|
||||
|
||||
let entry = elementAnalysers.get(audioElement)
|
||||
|
||||
if (!entry || entry.context.state === 'closed') {
|
||||
const context = new AudioContextCtor()
|
||||
if (!entry) {
|
||||
const context = getPlaybackAudioContext()
|
||||
|
||||
if (!context) {
|
||||
return null
|
||||
}
|
||||
|
||||
const source = context.createMediaElementSource(audioElement)
|
||||
const analyser = context.createAnalyser()
|
||||
|
||||
@@ -69,11 +84,11 @@ function getElementAnalyser(audioElement: HTMLAudioElement): ElementAnalyser | n
|
||||
analyser.smoothingTimeConstant = 0.65
|
||||
source.connect(analyser)
|
||||
analyser.connect(context.destination)
|
||||
entry = { analyser, context }
|
||||
entry = { analyser }
|
||||
elementAnalysers.set(audioElement, entry)
|
||||
}
|
||||
|
||||
void entry.context.resume()
|
||||
void playbackAudioContext?.resume()
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
@@ -111,11 +111,13 @@ function partText(part: unknown): string {
|
||||
return part
|
||||
}
|
||||
|
||||
if (part && typeof part === 'object' && 'text' in part && typeof part.text === 'string') {
|
||||
return part.text
|
||||
if (!part || typeof part !== 'object') {
|
||||
return ''
|
||||
}
|
||||
|
||||
return ''
|
||||
const row = part as { text?: unknown; type?: unknown }
|
||||
|
||||
return (!row.type || row.type === 'text') && typeof row.text === 'string' ? row.text : ''
|
||||
}
|
||||
|
||||
function messageContentText(content: unknown): string {
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
const EMOJI_RE = /[\p{Extended_Pictographic}\uFE0F\u200D]+/gu
|
||||
const EMOJI_RE =
|
||||
/(?:[\u{1F000}-\u{1FAFF}\u{2600}-\u{27BF}]|[\u{FE0F}\u{200D}]|[\u{E0020}-\u{E007F}])+/gu
|
||||
|
||||
const FENCED_CODE_RE = /```[\s\S]*?(?:```|$)/g
|
||||
const INLINE_CODE_RE = /`([^`]+)`/g
|
||||
const MARKDOWN_LINK_RE = /\[([^\]]+)\]\(([^)]+)\)/g
|
||||
const PARAGRAPH_BREAK_RE = /[ \t]*\n{2,}[ \t]*/g
|
||||
const SOFT_BREAK_RE = /[ \t]*\n[ \t]*/g
|
||||
|
||||
const THINKING_PREFIX_RE =
|
||||
/^\s*(?:\([^)\n]{1,48}\)\s*)?(?:processing|thinking|reasoning|analyzing|pondering|contemplating|musing|cogitating|ruminating|deliberating|mulling|reflecting|computing|synthesizing|formulating|brainstorming)\.\.\.\s*/i
|
||||
|
||||
const URL_RE = /\bhttps?:\/\/\S+/gi
|
||||
|
||||
export function sanitizeTextForSpeech(text: string): string {
|
||||
function normalizeLineBreaks(text: string): string {
|
||||
return text
|
||||
.replace(/\r\n?/g, '\n')
|
||||
.replace(/(\p{L})-\n(\p{L})/gu, '$1$2')
|
||||
.replace(PARAGRAPH_BREAK_RE, '. ')
|
||||
.replace(SOFT_BREAK_RE, ' ')
|
||||
}
|
||||
|
||||
export function sanitizeTextForSpeech(text: string): string {
|
||||
return normalizeLineBreaks(text)
|
||||
.replace(FENCED_CODE_RE, ' ')
|
||||
.replace(THINKING_PREFIX_RE, ' ')
|
||||
.replace(MARKDOWN_LINK_RE, '$1')
|
||||
.replace(INLINE_CODE_RE, '$1')
|
||||
.replace(URL_RE, ' link ')
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import { sanitizeTextForSpeech } from './speech-text'
|
||||
|
||||
let currentAudio: HTMLAudioElement | null = null
|
||||
let currentStop: (() => void) | null = null
|
||||
let sequence = 0
|
||||
|
||||
function currentState(
|
||||
@@ -32,10 +33,13 @@ export interface VoicePlaybackOptions {
|
||||
|
||||
export function stopVoicePlayback() {
|
||||
sequence += 1
|
||||
currentStop?.()
|
||||
currentStop = null
|
||||
|
||||
if (currentAudio) {
|
||||
currentAudio.pause()
|
||||
currentAudio.src = ''
|
||||
currentAudio.load()
|
||||
currentAudio = null
|
||||
}
|
||||
|
||||
@@ -74,8 +78,29 @@ export async function playSpeechText(text: string, options: VoicePlaybackOptions
|
||||
setVoicePlaybackState(currentState('speaking', options, audio))
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
audio.addEventListener('ended', () => resolve(), { once: true })
|
||||
audio.addEventListener('error', () => reject(new Error('Playback failed')), { once: true })
|
||||
const cleanup = () => {
|
||||
audio.removeEventListener('ended', onEnded)
|
||||
audio.removeEventListener('error', onError)
|
||||
currentStop = null
|
||||
}
|
||||
|
||||
const onEnded = () => {
|
||||
cleanup()
|
||||
resolve()
|
||||
}
|
||||
|
||||
const onError = () => {
|
||||
cleanup()
|
||||
reject(new Error('Playback failed'))
|
||||
}
|
||||
|
||||
currentStop = () => {
|
||||
cleanup()
|
||||
resolve()
|
||||
}
|
||||
|
||||
audio.addEventListener('ended', onEnded, { once: true })
|
||||
audio.addEventListener('error', onError, { once: true })
|
||||
void audio.play().catch(reject)
|
||||
})
|
||||
|
||||
@@ -89,6 +114,7 @@ export async function playSpeechText(text: string, options: VoicePlaybackOptions
|
||||
return true
|
||||
} catch (error) {
|
||||
if (isCurrent()) {
|
||||
currentStop = null
|
||||
currentAudio = null
|
||||
setVoicePlaybackState(currentState('idle'))
|
||||
}
|
||||
|
||||
@@ -186,155 +186,6 @@ button {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.composer-liquid-shell-wrap {
|
||||
pointer-events: none;
|
||||
border-radius: var(--composer-glass-radius, 20px);
|
||||
isolation: isolate;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Fragment siblings emitted by liquid-glass-react (not inside .composer-liquid-shell). */
|
||||
.composer-liquid-shell-wrap > div:not(.composer-liquid-shell) {
|
||||
position: absolute !important;
|
||||
inset: 0 !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: none !important;
|
||||
margin: 0 !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Liquid-glass rim spans (toggle via data-show-library-rims on shell wrap). */
|
||||
.composer-liquid-shell-wrap:not([data-show-library-rims='true']) > span {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell-wrap[data-show-library-rims='true'] > span {
|
||||
position: absolute !important;
|
||||
inset: 0 !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: none !important;
|
||||
margin: 0 !important;
|
||||
box-sizing: border-box;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell {
|
||||
z-index: 1;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
transform: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell > svg {
|
||||
position: absolute !important;
|
||||
inset: 0 !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell > .glass,
|
||||
.composer-liquid-shell > :not(svg):not(.glass) {
|
||||
position: absolute !important;
|
||||
inset: 0 !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell > .glass {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
padding: 0 !important;
|
||||
border-radius: var(--composer-glass-radius, 20px) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.composer-completion-drawer {
|
||||
position: absolute;
|
||||
inset-inline: 0;
|
||||
bottom: calc(100% - 0.5rem);
|
||||
z-index: 50;
|
||||
max-height: min(23rem, calc(100vh - 8rem));
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
border: 0.0625rem solid color-mix(in srgb, var(--dt-ring) 45%, transparent);
|
||||
border-bottom: 0;
|
||||
border-top-left-radius: var(--composer-active-radius);
|
||||
border-top-right-radius: var(--composer-active-radius);
|
||||
background: color-mix(in srgb, var(--dt-popover) 96%, transparent);
|
||||
color: var(--dt-popover-foreground);
|
||||
padding: 0.375rem 0.375rem 0.75rem;
|
||||
backdrop-filter: blur(0.75rem) saturate(1.1);
|
||||
-webkit-backdrop-filter: blur(0.75rem) saturate(1.1);
|
||||
}
|
||||
|
||||
.composer-completion-row {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
align-items: baseline;
|
||||
gap: 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
padding: 0.25rem 0.625rem;
|
||||
text-align: left;
|
||||
font-size: 0.75rem;
|
||||
transition: background-color 120ms ease;
|
||||
}
|
||||
|
||||
.composer-completion-row:hover,
|
||||
.composer-completion-row[data-highlighted] {
|
||||
background: color-mix(in srgb, var(--dt-accent) 70%, transparent);
|
||||
}
|
||||
|
||||
[data-slot='composer-root']:has([data-slot='composer-completion-drawer'][data-state='open'])
|
||||
[data-slot='composer-surface'] {
|
||||
border-top-left-radius: 0 !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
border-top-color: transparent;
|
||||
box-shadow:
|
||||
0 0.0625rem 0 0.0625rem color-mix(in srgb, var(--dt-ring) 35%, transparent),
|
||||
0 0.5rem 1.5rem color-mix(in srgb, var(--shadow-ink) 6%, transparent);
|
||||
}
|
||||
|
||||
[data-slot='composer-root']:has([data-slot='composer-completion-drawer'][data-state='open']) [data-glass-frame='true'] {
|
||||
border-top-left-radius: 0 !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
}
|
||||
|
||||
[data-slot='composer-root']:has([data-slot='composer-completion-drawer'][data-state='open'])
|
||||
[data-slot='composer-completion-drawer'] {
|
||||
margin-bottom: -0.5rem;
|
||||
border-bottom: 0;
|
||||
box-shadow:
|
||||
0 -0.0625rem 0 0.0625rem color-mix(in srgb, var(--dt-ring) 35%, transparent),
|
||||
0 -1rem 2.25rem -1.75rem color-mix(in srgb, var(--dt-foreground) 34%, transparent),
|
||||
0 -0.3125rem 0.875rem -0.6875rem color-mix(in srgb, var(--dt-foreground) 22%, transparent);
|
||||
}
|
||||
|
||||
.composer-liquid-shell > .glass > .glass__warp {
|
||||
border-radius: var(--composer-glass-radius, 20px) !important;
|
||||
}
|
||||
|
||||
.composer-liquid-shell > .glass > div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font: inherit !important;
|
||||
text-shadow: none !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea,
|
||||
[contenteditable]:not([contenteditable='false']),
|
||||
|
||||
Reference in New Issue
Block a user