mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-22 19:30:15 +01:00
feat: Enhance artifact panel animations and improve UI responsiveness
- Updated Thinking component button styles for smoother transitions. - Implemented dynamic rendering for artifacts panel with animation effects. - Refactored localization keys for consistency across multiple languages. - Added new CSS animations for iOS-inspired smooth transitions. - Improved Tailwind CSS configuration to support enhanced animation effects.
This commit is contained in:
parent
44fa479bd4
commit
7e1d02bcc3
35 changed files with 416 additions and 104 deletions
|
|
@ -55,40 +55,52 @@ const ArtifactButton = ({ artifact }: { artifact: Artifact | null }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group relative my-4 rounded-xl text-sm text-text-primary">
|
<div className="group relative my-4 rounded-xl text-sm text-text-primary">
|
||||||
<button
|
{(() => {
|
||||||
type="button"
|
const handleClick = () => {
|
||||||
onClick={() => {
|
if (!location.pathname.includes('/c/')) return;
|
||||||
if (!location.pathname.includes('/c/')) {
|
|
||||||
|
if (isSelected) {
|
||||||
|
resetCurrentArtifactId();
|
||||||
|
setVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
resetCurrentArtifactId();
|
resetCurrentArtifactId();
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
|
|
||||||
if (artifacts?.[artifact.id] == null) {
|
if (artifacts?.[artifact.id] == null) {
|
||||||
setArtifacts(visibleArtifacts);
|
setArtifacts(visibleArtifacts);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setCurrentArtifactId(artifact.id);
|
setCurrentArtifactId(artifact.id);
|
||||||
}, 15);
|
}, 15);
|
||||||
}}
|
};
|
||||||
className={
|
|
||||||
`relative overflow-hidden rounded-xl transition-all duration-200 hover:border-border-medium hover:bg-surface-hover hover:shadow-lg ` +
|
const buttonClass =
|
||||||
|
`relative overflow-hidden rounded-xl transition-all duration-300 hover:border-border-medium hover:bg-surface-hover hover:shadow-lg active:scale-[0.98] ` +
|
||||||
(isSelected
|
(isSelected
|
||||||
? 'border-border-medium bg-surface-hover shadow-lg'
|
? 'border-border-medium bg-surface-hover shadow-lg'
|
||||||
: 'border-border-light bg-surface-tertiary shadow-sm')
|
: 'border-border-light bg-surface-tertiary shadow-sm');
|
||||||
}
|
|
||||||
>
|
const actionLabel = isSelected
|
||||||
|
? localize('com_ui_click_to_close')
|
||||||
|
: localize('com_ui_click_to_open');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button type="button" onClick={handleClick} className={buttonClass}>
|
||||||
<div className="w-fit p-2">
|
<div className="w-fit p-2">
|
||||||
<div className="flex flex-row items-center gap-2">
|
<div className="flex flex-row items-center gap-2">
|
||||||
<FilePreview fileType={fileType} className="relative" />
|
<FilePreview fileType={fileType} className="relative" />
|
||||||
<div className="overflow-hidden text-left">
|
<div className="overflow-hidden text-left">
|
||||||
<div className="truncate font-medium">{artifact.title}</div>
|
<div className="truncate font-medium">{artifact.title}</div>
|
||||||
<div className="truncate text-text-secondary">
|
<div className="truncate text-text-secondary">{actionLabel}</div>
|
||||||
{localize('com_ui_artifact_click')}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
<br />
|
<br />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import React, { memo, useMemo, type MutableRefObject } from 'react';
|
import React, { memo, useMemo, type MutableRefObject } from 'react';
|
||||||
import { SandpackPreview, SandpackProvider } from '@codesandbox/sandpack-react/unstyled';
|
import { SandpackPreview, SandpackProvider } from '@codesandbox/sandpack-react/unstyled';
|
||||||
import type { SandpackProviderProps, SandpackPreviewRef, PreviewProps } from '@codesandbox/sandpack-react/unstyled';
|
import type {
|
||||||
|
SandpackProviderProps,
|
||||||
|
SandpackPreviewRef,
|
||||||
|
PreviewProps,
|
||||||
|
} from '@codesandbox/sandpack-react/unstyled';
|
||||||
import type { TStartupConfig } from 'librechat-data-provider';
|
import type { TStartupConfig } from 'librechat-data-provider';
|
||||||
import type { ArtifactFiles } from '~/common';
|
import type { ArtifactFiles } from '~/common';
|
||||||
import { sharedFiles, sharedOptions } from '~/utils/artifacts';
|
import { sharedFiles, sharedOptions } from '~/utils/artifacts';
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,16 @@ export default function ArtifactTabs({
|
||||||
const { files, fileKey, template, sharedProps } = useArtifactProps({ artifact });
|
const { files, fileKey, template, sharedProps } = useArtifactProps({ artifact });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="flex h-full w-full flex-col">
|
||||||
<Tabs.Content
|
<Tabs.Content
|
||||||
ref={contentRef}
|
ref={contentRef}
|
||||||
value="code"
|
value="code"
|
||||||
id="artifacts-code"
|
id="artifacts-code"
|
||||||
className={cn('flex-grow overflow-auto')}
|
className={cn(
|
||||||
|
'h-full w-full flex-grow overflow-auto',
|
||||||
|
'data-[state=active]:duration-200 data-[state=active]:animate-in data-[state=active]:fade-in-0',
|
||||||
|
'data-[state=inactive]:duration-150 data-[state=inactive]:animate-out data-[state=inactive]:fade-out-0',
|
||||||
|
)}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<ArtifactCodeEditor
|
<ArtifactCodeEditor
|
||||||
|
|
@ -56,7 +60,16 @@ export default function ArtifactTabs({
|
||||||
sharedProps={sharedProps}
|
sharedProps={sharedProps}
|
||||||
/>
|
/>
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
<Tabs.Content value="preview" className="flex-grow overflow-auto" tabIndex={-1}>
|
|
||||||
|
<Tabs.Content
|
||||||
|
value="preview"
|
||||||
|
className={cn(
|
||||||
|
'h-full w-full flex-grow overflow-auto',
|
||||||
|
'data-[state=active]:duration-200 data-[state=active]:animate-in data-[state=active]:fade-in-0',
|
||||||
|
'data-[state=inactive]:duration-150 data-[state=inactive]:animate-out data-[state=inactive]:fade-out-0',
|
||||||
|
)}
|
||||||
|
tabIndex={-1}
|
||||||
|
>
|
||||||
<ArtifactPreview
|
<ArtifactPreview
|
||||||
files={files}
|
files={files}
|
||||||
fileKey={fileKey}
|
fileKey={fileKey}
|
||||||
|
|
@ -67,6 +80,6 @@ export default function ArtifactTabs({
|
||||||
startupConfig={startupConfig}
|
startupConfig={startupConfig}
|
||||||
/>
|
/>
|
||||||
</Tabs.Content>
|
</Tabs.Content>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ export default function ArtifactVersion({
|
||||||
return (
|
return (
|
||||||
<DropdownPopup
|
<DropdownPopup
|
||||||
menuId={menuId}
|
menuId={menuId}
|
||||||
|
portal
|
||||||
focusLoop
|
focusLoop
|
||||||
unmountOnHide
|
unmountOnHide
|
||||||
isOpen={isPopoverActive}
|
isOpen={isPopoverActive}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useRef, useState, useEffect } from 'react';
|
import { useRef, useState, useEffect } from 'react';
|
||||||
import { useSetRecoilState } from 'recoil';
|
|
||||||
import * as Tabs from '@radix-ui/react-tabs';
|
import * as Tabs from '@radix-ui/react-tabs';
|
||||||
import { Code, Play, RefreshCw, X } from 'lucide-react';
|
import { Code, Play, RefreshCw, X } from 'lucide-react';
|
||||||
|
import { useSetRecoilState, useResetRecoilState } from 'recoil';
|
||||||
import { Button, Spinner, useMediaQuery } from '@librechat/client';
|
import { Button, Spinner, useMediaQuery } from '@librechat/client';
|
||||||
import type { SandpackPreviewRef, CodeEditorRef } from '@codesandbox/sandpack-react';
|
import type { SandpackPreviewRef, CodeEditorRef } from '@codesandbox/sandpack-react';
|
||||||
import useArtifacts from '~/hooks/Artifacts/useArtifacts';
|
import useArtifacts from '~/hooks/Artifacts/useArtifacts';
|
||||||
|
|
@ -21,12 +21,49 @@ export default function Artifacts() {
|
||||||
const editorRef = useRef<CodeEditorRef>();
|
const editorRef = useRef<CodeEditorRef>();
|
||||||
const previewRef = useRef<SandpackPreviewRef>();
|
const previewRef = useRef<SandpackPreviewRef>();
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
const [isClosing, setIsClosing] = useState(false);
|
||||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||||
|
const [isMounted, setIsMounted] = useState(false);
|
||||||
|
const [height, setHeight] = useState(90); // Height as percentage of viewport
|
||||||
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
|
const [blurAmount, setBlurAmount] = useState(0); // Dynamic blur amount
|
||||||
|
const dragStartY = useRef(0);
|
||||||
|
const dragStartHeight = useRef(90);
|
||||||
const setArtifactsVisible = useSetRecoilState(store.artifactsVisibility);
|
const setArtifactsVisible = useSetRecoilState(store.artifactsVisibility);
|
||||||
|
const resetCurrentArtifactId = useResetRecoilState(store.currentArtifactId);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsVisible(true);
|
setIsMounted(true);
|
||||||
}, []);
|
const delay = isMobile ? 50 : 30;
|
||||||
|
const timer = setTimeout(() => setIsVisible(true), delay);
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
setIsMounted(false);
|
||||||
|
};
|
||||||
|
}, [isMobile]);
|
||||||
|
|
||||||
|
// Dynamic blur based on height - more blur when taking up more screen
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isMobile) {
|
||||||
|
setBlurAmount(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate blur amount based on how much screen is covered
|
||||||
|
// 50% height = no blur, 100% height = full blur
|
||||||
|
const minHeightForBlur = 50;
|
||||||
|
const maxHeightForBlur = 100;
|
||||||
|
|
||||||
|
if (height <= minHeightForBlur) {
|
||||||
|
setBlurAmount(0);
|
||||||
|
} else if (height >= maxHeightForBlur) {
|
||||||
|
setBlurAmount(32); // Increased from 16 to 32 for stronger blur
|
||||||
|
} else {
|
||||||
|
// Linear interpolation between 0 and 32px blur
|
||||||
|
const progress = (height - minHeightForBlur) / (maxHeightForBlur - minHeightForBlur);
|
||||||
|
setBlurAmount(Math.round(progress * 32)); // Changed from 16 to 32
|
||||||
|
}
|
||||||
|
}, [height, isMobile]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
activeTab,
|
activeTab,
|
||||||
|
|
@ -37,7 +74,47 @@ export default function Artifacts() {
|
||||||
setCurrentArtifactId,
|
setCurrentArtifactId,
|
||||||
} = useArtifacts();
|
} = useArtifacts();
|
||||||
|
|
||||||
if (!currentArtifact) {
|
const handleDragStart = (e: React.PointerEvent) => {
|
||||||
|
setIsDragging(true);
|
||||||
|
dragStartY.current = e.clientY;
|
||||||
|
dragStartHeight.current = height;
|
||||||
|
(e.target as HTMLElement).setPointerCapture(e.pointerId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragMove = (e: React.PointerEvent) => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
|
||||||
|
const deltaY = dragStartY.current - e.clientY;
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
const deltaPercentage = (deltaY / viewportHeight) * 100;
|
||||||
|
const newHeight = Math.max(10, Math.min(100, dragStartHeight.current + deltaPercentage));
|
||||||
|
|
||||||
|
setHeight(newHeight);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragEnd = (e: React.PointerEvent) => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
|
||||||
|
setIsDragging(false);
|
||||||
|
(e.target as HTMLElement).releasePointerCapture(e.pointerId);
|
||||||
|
|
||||||
|
// Snap to positions based on final height
|
||||||
|
if (height < 30) {
|
||||||
|
// Close if dragged down significantly
|
||||||
|
closeArtifacts();
|
||||||
|
} else if (height > 95) {
|
||||||
|
// Snap to full height if dragged near top
|
||||||
|
setHeight(100);
|
||||||
|
} else if (height < 60) {
|
||||||
|
// Snap to minimum if in lower range
|
||||||
|
setHeight(50);
|
||||||
|
} else {
|
||||||
|
// Snap to default
|
||||||
|
setHeight(90);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!currentArtifact || !isMounted) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,57 +128,97 @@ export default function Artifacts() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeArtifacts = () => {
|
const closeArtifacts = () => {
|
||||||
|
if (isMobile) {
|
||||||
|
setIsClosing(true);
|
||||||
setIsVisible(false);
|
setIsVisible(false);
|
||||||
setTimeout(() => setArtifactsVisible(false), isMobile ? 400 : 500);
|
setTimeout(() => {
|
||||||
|
setArtifactsVisible(false);
|
||||||
|
setIsClosing(false);
|
||||||
|
setHeight(90); // Reset height
|
||||||
|
}, 250);
|
||||||
|
} else {
|
||||||
|
resetCurrentArtifactId();
|
||||||
|
setArtifactsVisible(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Calculate backdrop opacity based on blur amount
|
||||||
|
const backdropOpacity = blurAmount > 0 ? Math.min(0.3, blurAmount / 53.33) : 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs.Root value={activeTab} onValueChange={setActiveTab} asChild>
|
<Tabs.Root value={activeTab} onValueChange={setActiveTab} asChild>
|
||||||
<div className="flex h-full w-full flex-col">
|
<div className="flex h-full w-full flex-col">
|
||||||
{/* Mobile backdrop */}
|
{/* Mobile backdrop with dynamic blur */}
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'duration-400 ease-[cubic-bezier(0.25,0.46,0.45,0.94)] fixed inset-0 z-[99] bg-black/40 backdrop-blur-md transition-opacity',
|
'fixed inset-0 z-[99] bg-black will-change-[opacity,backdrop-filter]',
|
||||||
isVisible ? 'opacity-100' : 'pointer-events-none opacity-0',
|
isVisible && !isClosing
|
||||||
|
? 'transition-all duration-300'
|
||||||
|
: 'pointer-events-none opacity-0 backdrop-blur-none transition-opacity duration-150',
|
||||||
|
// Allow pointer events when not fully blurred so chat is scrollable
|
||||||
|
blurAmount < 8 && isVisible && !isClosing ? 'pointer-events-none' : '',
|
||||||
)}
|
)}
|
||||||
onClick={closeArtifacts}
|
style={{
|
||||||
|
opacity: isVisible && !isClosing ? backdropOpacity : 0,
|
||||||
|
backdropFilter: isVisible && !isClosing ? `blur(${blurAmount}px)` : 'none',
|
||||||
|
WebkitBackdropFilter: isVisible && !isClosing ? `blur(${blurAmount}px)` : 'none',
|
||||||
|
}}
|
||||||
|
onClick={blurAmount >= 8 ? closeArtifacts : undefined}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex h-full w-full flex-col overflow-hidden bg-surface-primary text-xl text-text-primary',
|
'flex w-full flex-col bg-surface-primary text-xl text-text-primary',
|
||||||
isMobile
|
isMobile
|
||||||
? cn(
|
? cn(
|
||||||
'duration-400 ease-[cubic-bezier(0.25,0.46,0.45,0.94)] fixed inset-x-0 bottom-0 z-[100] h-[90vh] rounded-t-[20px] shadow-[0_-10px_40px_rgba(0,0,0,0.3)] transition-transform will-change-transform',
|
'fixed inset-x-0 bottom-0 z-[100] rounded-t-[20px] shadow-[0_-10px_60px_rgba(0,0,0,0.35)]',
|
||||||
isVisible ? 'translate-y-0' : 'translate-y-full',
|
isVisible && !isClosing
|
||||||
|
? 'translate-y-0 opacity-100'
|
||||||
|
: 'duration-250 translate-y-full opacity-0 transition-all',
|
||||||
|
isDragging ? '' : 'transition-all duration-300',
|
||||||
)
|
)
|
||||||
: cn(
|
: cn(
|
||||||
'ease-[cubic-bezier(0.25,0.46,0.45,0.94)] shadow-xl transition-all duration-500 will-change-transform',
|
'h-full shadow-2xl',
|
||||||
isVisible ? 'translate-x-0 opacity-100' : 'translate-x-full opacity-0',
|
isVisible && !isClosing
|
||||||
|
? 'duration-350 translate-x-0 opacity-100 transition-all'
|
||||||
|
: 'translate-x-5 opacity-0 transition-all duration-300',
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
|
style={isMobile ? { height: `${height}vh` } : { overflow: 'hidden' }}
|
||||||
>
|
>
|
||||||
{/* Mobile drag indicator */}
|
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<div className="flex flex-shrink-0 items-center justify-center pb-1.5 pt-2">
|
<div
|
||||||
<div className="h-1 w-10 rounded-full bg-border-medium opacity-50" />
|
className="flex flex-shrink-0 cursor-grab items-center justify-center bg-surface-primary-alt pb-1.5 pt-2.5 active:cursor-grabbing"
|
||||||
|
onPointerDown={handleDragStart}
|
||||||
|
onPointerMove={handleDragMove}
|
||||||
|
onPointerUp={handleDragEnd}
|
||||||
|
onPointerCancel={handleDragEnd}
|
||||||
|
>
|
||||||
|
<div className="h-1 w-12 rounded-full bg-border-xheavy opacity-40 transition-all duration-200 active:opacity-60" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex flex-shrink-0 items-center justify-between gap-2 overflow-x-auto border-b border-border-light bg-surface-primary-alt px-3 py-2',
|
'flex flex-shrink-0 items-center justify-between gap-2 border-b border-border-light bg-surface-primary-alt px-3 py-2 transition-all duration-300',
|
||||||
isMobile && 'justify-center',
|
isMobile ? 'justify-center' : 'overflow-hidden',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<div className="flex items-center">
|
<div
|
||||||
|
className={cn(
|
||||||
|
'flex items-center transition-all duration-500',
|
||||||
|
isVisible && !isClosing
|
||||||
|
? 'translate-x-0 opacity-100'
|
||||||
|
: '-translate-x-2 opacity-0',
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Tabs.List className="relative inline-flex h-9 gap-2 rounded-xl bg-surface-tertiary p-0.5">
|
<Tabs.List className="relative inline-flex h-9 gap-2 rounded-xl bg-surface-tertiary p-0.5">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'absolute top-0.5 h-8 rounded-xl bg-surface-primary-alt transition-transform duration-200 ease-out',
|
'duration-[350ms] absolute top-0.5 h-8 rounded-[10px] bg-surface-primary-alt shadow-sm transition-all will-change-transform',
|
||||||
activeTab === 'code'
|
activeTab === 'code'
|
||||||
? 'w-[42%] translate-x-0'
|
? 'w-[42%] translate-x-0'
|
||||||
: 'w-[50%] translate-x-[calc(100%-0.250rem)]',
|
: 'w-[50%] translate-x-[calc(100%-0.250rem)]',
|
||||||
|
|
@ -109,24 +226,30 @@ export default function Artifacts() {
|
||||||
/>
|
/>
|
||||||
<Tabs.Trigger
|
<Tabs.Trigger
|
||||||
value="code"
|
value="code"
|
||||||
className="relative z-10 flex items-center gap-1.5 rounded-xl border-transparent px-3 py-1 text-xs font-medium transition-all duration-200 ease-out hover:text-text-primary data-[state=active]:text-text-primary data-[state=inactive]:text-text-secondary"
|
className="relative z-10 flex items-center gap-1.5 rounded-[10px] border-transparent px-3 py-1 text-xs font-medium transition-all duration-200 hover:text-text-primary active:scale-95 data-[state=active]:text-text-primary data-[state=inactive]:text-text-secondary"
|
||||||
>
|
>
|
||||||
<Code className="size-3" />
|
<Code className="size-3 transition-transform duration-200 group-active:scale-90" />
|
||||||
<span>{localize('com_ui_code')}</span>
|
<span className="whitespace-nowrap">{localize('com_ui_code')}</span>
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
<Tabs.Trigger
|
<Tabs.Trigger
|
||||||
value="preview"
|
value="preview"
|
||||||
disabled={isMutating}
|
disabled={isMutating}
|
||||||
className="relative z-10 flex items-center gap-1.5 rounded-xl border-transparent px-3 py-1 text-xs font-medium transition-all duration-200 ease-out hover:text-text-primary disabled:cursor-not-allowed disabled:opacity-50 data-[state=active]:text-text-primary data-[state=inactive]:text-text-secondary"
|
className="relative z-10 flex items-center gap-1.5 rounded-[10px] border-transparent px-3 py-1 text-xs font-medium transition-all duration-200 hover:text-text-primary active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 data-[state=active]:text-text-primary data-[state=inactive]:text-text-secondary"
|
||||||
>
|
>
|
||||||
<Play className="size-3" />
|
<Play className="size-3 transition-transform duration-200 group-active:scale-90" />
|
||||||
<span>{localize('com_ui_preview')}</span>
|
<span className="whitespace-nowrap">{localize('com_ui_preview')}</span>
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex min-w-max items-center gap-2">
|
<div
|
||||||
|
className={cn(
|
||||||
|
'flex items-center gap-2 transition-all duration-500',
|
||||||
|
isMobile ? 'min-w-max' : '',
|
||||||
|
isVisible && !isClosing ? 'translate-x-0 opacity-100' : 'translate-x-2 opacity-0',
|
||||||
|
)}
|
||||||
|
>
|
||||||
{activeTab === 'preview' && (
|
{activeTab === 'preview' && (
|
||||||
<Button
|
<Button
|
||||||
size="icon"
|
size="icon"
|
||||||
|
|
@ -134,9 +257,13 @@ export default function Artifacts() {
|
||||||
onClick={handleRefresh}
|
onClick={handleRefresh}
|
||||||
disabled={isRefreshing}
|
disabled={isRefreshing}
|
||||||
aria-label={localize('com_ui_refresh')}
|
aria-label={localize('com_ui_refresh')}
|
||||||
className="h-8 w-8 transition-transform duration-150 ease-out hover:scale-105 active:scale-95"
|
className="transition-all duration-150 active:scale-90"
|
||||||
>
|
>
|
||||||
{isRefreshing ? <Spinner size={16} /> : <RefreshCw size={16} />}
|
{isRefreshing ? (
|
||||||
|
<Spinner size={16} />
|
||||||
|
) : (
|
||||||
|
<RefreshCw size={16} className="transition-transform duration-200" />
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{activeTab !== 'preview' && isMutating && (
|
{activeTab !== 'preview' && isMutating && (
|
||||||
|
|
@ -161,35 +288,37 @@ export default function Artifacts() {
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={closeArtifacts}
|
onClick={closeArtifacts}
|
||||||
aria-label={localize('com_ui_close')}
|
aria-label={localize('com_ui_close')}
|
||||||
className="h-8 w-8 transition-transform duration-150 ease-out hover:scale-105 active:scale-95"
|
className="h-8 w-8 transition-all duration-150 hover:scale-105 active:scale-90"
|
||||||
>
|
>
|
||||||
<X size={16} />
|
<X size={16} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content Area - This is the key fix */}
|
{/* Content Area - Fixed positioning to prevent layout shifts */}
|
||||||
<div className="relative flex min-h-0 flex-1 flex-col overflow-hidden">
|
<div className="relative flex min-h-0 flex-1 flex-col overflow-hidden bg-surface-primary">
|
||||||
|
<div className="absolute inset-0 flex flex-col">
|
||||||
<ArtifactTabs
|
<ArtifactTabs
|
||||||
artifact={currentArtifact}
|
artifact={currentArtifact}
|
||||||
editorRef={editorRef as React.MutableRefObject<CodeEditorRef>}
|
editorRef={editorRef as React.MutableRefObject<CodeEditorRef>}
|
||||||
previewRef={previewRef as React.MutableRefObject<SandpackPreviewRef>}
|
previewRef={previewRef as React.MutableRefObject<SandpackPreviewRef>}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Mobile Tab Switcher */}
|
{/* Mobile Tab Switcher with iOS-style animation */}
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<div className="pb-safe-offset-3 flex-shrink-0 border-t border-border-light bg-surface-primary-alt px-3 pt-2">
|
<div className="pb-safe-offset-3 flex-shrink-0 border-t border-border-light bg-surface-primary-alt px-3 pt-2">
|
||||||
<Tabs.List className="relative flex h-10 w-full rounded-xl bg-surface-tertiary p-1">
|
<Tabs.List className="relative flex h-10 w-full rounded-xl bg-surface-tertiary p-1">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'duration-[250ms] ease-[cubic-bezier(0.25,0.46,0.45,0.94)] absolute left-1 top-1 h-8 w-[calc(50%-0.25rem)] rounded-lg bg-surface-primary-alt shadow-sm transition-transform',
|
'duration-[350ms] absolute left-1 top-1 h-8 w-[calc(50%-0.25rem)] rounded-[10px] bg-surface-primary-alt shadow-sm transition-transform will-change-transform',
|
||||||
activeTab === 'code' ? 'translate-x-0' : 'translate-x-[calc(100%+0.5rem)]',
|
activeTab === 'code' ? 'translate-x-0' : 'translate-x-[calc(100%+0.5rem)]',
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Tabs.Trigger
|
<Tabs.Trigger
|
||||||
value="code"
|
value="code"
|
||||||
className="relative z-10 flex w-1/2 items-center justify-center gap-1.5 rounded-lg border-transparent py-1.5 text-xs font-medium transition-all duration-150 ease-out active:scale-95 data-[state=active]:text-text-primary data-[state=inactive]:text-text-secondary"
|
className="relative z-10 flex w-1/2 items-center justify-center gap-1.5 rounded-[10px] border-transparent py-1.5 text-xs font-medium transition-all duration-150 active:scale-95 data-[state=active]:text-text-primary data-[state=inactive]:text-text-secondary"
|
||||||
>
|
>
|
||||||
<Code className="size-3.5" />
|
<Code className="size-3.5" />
|
||||||
<span>{localize('com_ui_code')}</span>
|
<span>{localize('com_ui_code')}</span>
|
||||||
|
|
@ -197,7 +326,7 @@ export default function Artifacts() {
|
||||||
<Tabs.Trigger
|
<Tabs.Trigger
|
||||||
value="preview"
|
value="preview"
|
||||||
disabled={isMutating}
|
disabled={isMutating}
|
||||||
className="relative z-10 flex w-1/2 items-center justify-center gap-1.5 rounded-lg border-transparent py-1.5 text-xs font-medium transition-all duration-150 ease-out active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 data-[state=active]:text-text-primary data-[state=inactive]:text-text-secondary"
|
className="relative z-10 flex w-1/2 items-center justify-center gap-1.5 rounded-[10px] border-transparent py-1.5 text-xs font-medium transition-all duration-150 active:scale-95 disabled:cursor-not-allowed disabled:opacity-50 data-[state=active]:text-text-primary data-[state=inactive]:text-text-secondary"
|
||||||
>
|
>
|
||||||
<Play className="size-3.5" />
|
<Play className="size-3.5" />
|
||||||
<span>{localize('com_ui_preview')}</span>
|
<span>{localize('com_ui_preview')}</span>
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ import { cn } from '~/utils';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
const BUTTON_STYLES = {
|
const BUTTON_STYLES = {
|
||||||
base: 'group mt-3 flex w-fit items-center justify-center rounded-xl bg-surface-tertiary px-3 py-2 text-xs leading-[18px] animate-thinking-appear',
|
base: '-sring group mt-3 flex w-fit items-center justify-center rounded-xl bg-surface-tertiary px-3 py-2 text-xs leading-[18px] transition-all duration-300 hover:bg-surface-secondary active:scale-95 animate-thinking-appear',
|
||||||
icon: 'icon-sm ml-1.5 transform-gpu text-text-primary transition-transform duration-200',
|
icon: 'icon-sm ml-1.5 transform-gpu text-text-primary transition-transform duration-300',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const CONTENT_STYLES = {
|
const CONTENT_STYLES = {
|
||||||
|
|
@ -69,7 +69,7 @@ const Thinking: React.ElementType = memo(({ children }: { children: React.ReactN
|
||||||
<ThinkingButton isExpanded={isExpanded} onClick={handleClick} label={label} />
|
<ThinkingButton isExpanded={isExpanded} onClick={handleClick} label={label} />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={cn('grid transition-all duration-300 ease-out', isExpanded && 'mb-8')}
|
className={cn('duration-400 grid transition-all', isExpanded && 'mb-8')}
|
||||||
style={{
|
style={{
|
||||||
gridTemplateRows: isExpanded ? '1fr' : '0fr',
|
gridTemplateRows: isExpanded ? '1fr' : '0fr',
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ import { normalizeLayout } from '~/utils';
|
||||||
import SidePanel from './SidePanel';
|
import SidePanel from './SidePanel';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
|
const ANIMATION_DURATION = 500;
|
||||||
|
|
||||||
interface SidePanelProps {
|
interface SidePanelProps {
|
||||||
defaultLayout?: number[] | undefined;
|
defaultLayout?: number[] | undefined;
|
||||||
defaultCollapsed?: boolean;
|
defaultCollapsed?: boolean;
|
||||||
|
|
@ -42,14 +44,43 @@ const SidePanelGroup = memo(
|
||||||
);
|
);
|
||||||
|
|
||||||
const panelRef = useRef<ImperativePanelHandle>(null);
|
const panelRef = useRef<ImperativePanelHandle>(null);
|
||||||
|
const artifactsPanelRef = useRef<ImperativePanelHandle>(null);
|
||||||
const [minSize, setMinSize] = useState(defaultMinSize);
|
const [minSize, setMinSize] = useState(defaultMinSize);
|
||||||
const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
|
const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
|
||||||
const [fullCollapse, setFullCollapse] = useState(fullPanelCollapse);
|
const [fullCollapse, setFullCollapse] = useState(fullPanelCollapse);
|
||||||
const [collapsedSize, setCollapsedSize] = useState(navCollapsedSize);
|
const [collapsedSize, setCollapsedSize] = useState(navCollapsedSize);
|
||||||
|
const [shouldRenderArtifacts, setShouldRenderArtifacts] = useState(artifacts != null);
|
||||||
|
const artifactsTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
const isSmallScreen = useMediaQuery('(max-width: 767px)');
|
const isSmallScreen = useMediaQuery('(max-width: 767px)');
|
||||||
const hideSidePanel = useRecoilValue(store.hideSidePanel);
|
const hideSidePanel = useRecoilValue(store.hideSidePanel);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (artifacts != null) {
|
||||||
|
if (artifactsTimeoutRef.current) {
|
||||||
|
clearTimeout(artifactsTimeoutRef.current);
|
||||||
|
artifactsTimeoutRef.current = null;
|
||||||
|
}
|
||||||
|
setShouldRenderArtifacts(true);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
artifactsPanelRef.current?.expand();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (shouldRenderArtifacts) {
|
||||||
|
artifactsPanelRef.current?.collapse();
|
||||||
|
artifactsTimeoutRef.current = setTimeout(() => {
|
||||||
|
setShouldRenderArtifacts(false);
|
||||||
|
}, ANIMATION_DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (artifactsTimeoutRef.current) {
|
||||||
|
clearTimeout(artifactsTimeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [artifacts, shouldRenderArtifacts]);
|
||||||
|
|
||||||
const calculateLayout = useCallback(() => {
|
const calculateLayout = useCallback(() => {
|
||||||
if (artifacts == null) {
|
if (artifacts == null) {
|
||||||
const navSize = defaultLayout.length === 2 ? defaultLayout[1] : defaultLayout[2];
|
const navSize = defaultLayout.length === 2 ? defaultLayout[1] : defaultLayout[2];
|
||||||
|
|
@ -120,20 +151,25 @@ const SidePanelGroup = memo(
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
{artifacts != null && !isSmallScreen && (
|
{shouldRenderArtifacts && !isSmallScreen && (
|
||||||
<>
|
<>
|
||||||
|
{artifacts != null && (
|
||||||
<ResizableHandleAlt
|
<ResizableHandleAlt
|
||||||
withHandle
|
withHandle
|
||||||
className="ml-3 bg-border-medium text-text-primary transition-opacity duration-300"
|
className="ml-3 bg-border-medium text-text-primary"
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
<ResizablePanel
|
<ResizablePanel
|
||||||
defaultSize={currentLayout[1]}
|
ref={artifactsPanelRef}
|
||||||
|
defaultSize={artifacts != null ? currentLayout[1] : 0}
|
||||||
minSize={minSizeMain}
|
minSize={minSizeMain}
|
||||||
|
collapsible={true}
|
||||||
|
collapsedSize={0}
|
||||||
order={2}
|
order={2}
|
||||||
id="artifacts-panel"
|
id="artifacts-panel"
|
||||||
className="ease-[cubic-bezier(0.25,0.46,0.45,0.94)] transition-all duration-500"
|
className="ease-[cubic-bezier(0.25,0.46,0.45,0.94)] transition-all duration-500"
|
||||||
>
|
>
|
||||||
{artifacts}
|
<div className="h-full min-w-[400px] overflow-hidden">{artifacts}</div>
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -427,7 +427,7 @@
|
||||||
"com_ui_all_proper": "الكل",
|
"com_ui_all_proper": "الكل",
|
||||||
"com_ui_archive": "أرشفة",
|
"com_ui_archive": "أرشفة",
|
||||||
"com_ui_archive_error": "فشل في أرشفة المحادثة",
|
"com_ui_archive_error": "فشل في أرشفة المحادثة",
|
||||||
"com_ui_artifact_click": "انقر للفتح",
|
"com_ui_click_to_open": "انقر للفتح",
|
||||||
"com_ui_artifacts": "المخرجات",
|
"com_ui_artifacts": "المخرجات",
|
||||||
"com_ui_artifacts_toggle": "تبديل واجهة العناصر",
|
"com_ui_artifacts_toggle": "تبديل واجهة العناصر",
|
||||||
"com_ui_ascending": "تصاعدي",
|
"com_ui_ascending": "تصاعدي",
|
||||||
|
|
|
||||||
|
|
@ -508,7 +508,7 @@
|
||||||
"com_ui_archive": "Arxiva",
|
"com_ui_archive": "Arxiva",
|
||||||
"com_ui_archive_delete_error": "No s'ha pogut eliminar la conversa arxivada",
|
"com_ui_archive_delete_error": "No s'ha pogut eliminar la conversa arxivada",
|
||||||
"com_ui_archive_error": "No s'ha pogut arxivar la conversa",
|
"com_ui_archive_error": "No s'ha pogut arxivar la conversa",
|
||||||
"com_ui_artifact_click": "Fes clic per obrir",
|
"com_ui_click_to_open": "Fes clic per obrir",
|
||||||
"com_ui_artifacts": "Artifacts",
|
"com_ui_artifacts": "Artifacts",
|
||||||
"com_ui_artifacts_toggle": "Activa/desactiva la UI d'artifacts",
|
"com_ui_artifacts_toggle": "Activa/desactiva la UI d'artifacts",
|
||||||
"com_ui_artifacts_toggle_agent": "Habilita artifacts",
|
"com_ui_artifacts_toggle_agent": "Habilita artifacts",
|
||||||
|
|
|
||||||
|
|
@ -385,7 +385,7 @@
|
||||||
"com_ui_api_key": "API klíč",
|
"com_ui_api_key": "API klíč",
|
||||||
"com_ui_archive": "Archivovat",
|
"com_ui_archive": "Archivovat",
|
||||||
"com_ui_archive_error": "Nepodařilo se archivovat konverzaci",
|
"com_ui_archive_error": "Nepodařilo se archivovat konverzaci",
|
||||||
"com_ui_artifact_click": "Klikněte pro otevření",
|
"com_ui_click_to_open": "Klikněte pro otevření",
|
||||||
"com_ui_artifacts": "Artefakty",
|
"com_ui_artifacts": "Artefakty",
|
||||||
"com_ui_artifacts_toggle": "Přepnout uživatelské rozhraní artefaktů",
|
"com_ui_artifacts_toggle": "Přepnout uživatelské rozhraní artefaktů",
|
||||||
"com_ui_artifacts_toggle_agent": "Povolit artefakty",
|
"com_ui_artifacts_toggle_agent": "Povolit artefakty",
|
||||||
|
|
|
||||||
|
|
@ -531,7 +531,7 @@
|
||||||
"com_ui_archive": "Arkiv",
|
"com_ui_archive": "Arkiv",
|
||||||
"com_ui_archive_delete_error": "Kunne ikke slette arkiveret samtale",
|
"com_ui_archive_delete_error": "Kunne ikke slette arkiveret samtale",
|
||||||
"com_ui_archive_error": "Kunne ikke arkivere samtale",
|
"com_ui_archive_error": "Kunne ikke arkivere samtale",
|
||||||
"com_ui_artifact_click": "Klik for at åbne",
|
"com_ui_click_to_open": "Klik for at åbne",
|
||||||
"com_ui_artifacts": "Artefakter",
|
"com_ui_artifacts": "Artefakter",
|
||||||
"com_ui_artifacts_toggle": "Skift artefakter UI",
|
"com_ui_artifacts_toggle": "Skift artefakter UI",
|
||||||
"com_ui_artifacts_toggle_agent": "Aktiver artefakter",
|
"com_ui_artifacts_toggle_agent": "Aktiver artefakter",
|
||||||
|
|
|
||||||
|
|
@ -685,7 +685,7 @@
|
||||||
"com_ui_archive": "Archivieren",
|
"com_ui_archive": "Archivieren",
|
||||||
"com_ui_archive_delete_error": "Archivierter Chat konnte nicht gelöscht werden.",
|
"com_ui_archive_delete_error": "Archivierter Chat konnte nicht gelöscht werden.",
|
||||||
"com_ui_archive_error": "Konversation konnte nicht archiviert werden",
|
"com_ui_archive_error": "Konversation konnte nicht archiviert werden",
|
||||||
"com_ui_artifact_click": "Zum Öffnen klicken",
|
"com_ui_click_to_open": "Zum Öffnen klicken",
|
||||||
"com_ui_artifacts": "Artefakte",
|
"com_ui_artifacts": "Artefakte",
|
||||||
"com_ui_artifacts_options": "Artefakt Optionen",
|
"com_ui_artifacts_options": "Artefakt Optionen",
|
||||||
"com_ui_artifacts_toggle": "Artefakte-Funktion einschalten",
|
"com_ui_artifacts_toggle": "Artefakte-Funktion einschalten",
|
||||||
|
|
|
||||||
|
|
@ -688,7 +688,8 @@
|
||||||
"com_ui_archive": "Archive",
|
"com_ui_archive": "Archive",
|
||||||
"com_ui_archive_delete_error": "Failed to delete archived conversation",
|
"com_ui_archive_delete_error": "Failed to delete archived conversation",
|
||||||
"com_ui_archive_error": "Failed to archive conversation",
|
"com_ui_archive_error": "Failed to archive conversation",
|
||||||
"com_ui_artifact_click": "Click to open",
|
"com_ui_click_to_open": "Click to open",
|
||||||
|
"com_ui_click_to_close": "Click to close",
|
||||||
"com_ui_artifacts": "Artifacts",
|
"com_ui_artifacts": "Artifacts",
|
||||||
"com_ui_artifacts_options": "Artifacts Options",
|
"com_ui_artifacts_options": "Artifacts Options",
|
||||||
"com_ui_artifacts_toggle": "Toggle Artifacts UI",
|
"com_ui_artifacts_toggle": "Toggle Artifacts UI",
|
||||||
|
|
|
||||||
|
|
@ -510,7 +510,7 @@
|
||||||
"com_ui_archive": "Archivar",
|
"com_ui_archive": "Archivar",
|
||||||
"com_ui_archive_delete_error": "Error al borrar conversación archivada",
|
"com_ui_archive_delete_error": "Error al borrar conversación archivada",
|
||||||
"com_ui_archive_error": "Error al archivar la conversación",
|
"com_ui_archive_error": "Error al archivar la conversación",
|
||||||
"com_ui_artifact_click": "Haga clic para abrir",
|
"com_ui_click_to_open": "Haga clic para abrir",
|
||||||
"com_ui_artifacts": "Artefactos",
|
"com_ui_artifacts": "Artefactos",
|
||||||
"com_ui_artifacts_options": "Opciones de artefactos",
|
"com_ui_artifacts_options": "Opciones de artefactos",
|
||||||
"com_ui_artifacts_toggle": "Alternar Interfaz de Artefactos",
|
"com_ui_artifacts_toggle": "Alternar Interfaz de Artefactos",
|
||||||
|
|
|
||||||
|
|
@ -532,7 +532,7 @@
|
||||||
"com_ui_archive": "Arhiveeri",
|
"com_ui_archive": "Arhiveeri",
|
||||||
"com_ui_archive_delete_error": "Arhiveeritud vestluse kustutamine ebaõnnestus",
|
"com_ui_archive_delete_error": "Arhiveeritud vestluse kustutamine ebaõnnestus",
|
||||||
"com_ui_archive_error": "Vestluse arhiveerimine ebaõnnestus",
|
"com_ui_archive_error": "Vestluse arhiveerimine ebaõnnestus",
|
||||||
"com_ui_artifact_click": "Klõpsa avamiseks",
|
"com_ui_click_to_open": "Klõpsa avamiseks",
|
||||||
"com_ui_artifacts": "Artefaktid",
|
"com_ui_artifacts": "Artefaktid",
|
||||||
"com_ui_artifacts_toggle": "Lülita artefaktide kasutajaliides sisse/välja",
|
"com_ui_artifacts_toggle": "Lülita artefaktide kasutajaliides sisse/välja",
|
||||||
"com_ui_artifacts_toggle_agent": "Luba artefaktid",
|
"com_ui_artifacts_toggle_agent": "Luba artefaktid",
|
||||||
|
|
|
||||||
|
|
@ -479,7 +479,7 @@
|
||||||
"com_ui_api_key": "کلید API",
|
"com_ui_api_key": "کلید API",
|
||||||
"com_ui_archive": "آرشیو",
|
"com_ui_archive": "آرشیو",
|
||||||
"com_ui_archive_error": "مکالمه بایگانی نشد",
|
"com_ui_archive_error": "مکالمه بایگانی نشد",
|
||||||
"com_ui_artifact_click": "برای باز کردن کلیک کنید",
|
"com_ui_click_to_open": "برای باز کردن کلیک کنید",
|
||||||
"com_ui_artifacts": "مصنوعات",
|
"com_ui_artifacts": "مصنوعات",
|
||||||
"com_ui_artifacts_toggle": "تغییر رابط کاربری Artifacts",
|
"com_ui_artifacts_toggle": "تغییر رابط کاربری Artifacts",
|
||||||
"com_ui_artifacts_toggle_agent": "Artifacts را فعال کنید",
|
"com_ui_artifacts_toggle_agent": "Artifacts را فعال کنید",
|
||||||
|
|
|
||||||
|
|
@ -635,7 +635,7 @@
|
||||||
"com_ui_archive": "Archiver",
|
"com_ui_archive": "Archiver",
|
||||||
"com_ui_archive_delete_error": "Suppression de la conversation archivée échouée",
|
"com_ui_archive_delete_error": "Suppression de la conversation archivée échouée",
|
||||||
"com_ui_archive_error": "échec de l'archivage de la conversation",
|
"com_ui_archive_error": "échec de l'archivage de la conversation",
|
||||||
"com_ui_artifact_click": "Cliquer pour ouvrir",
|
"com_ui_click_to_open": "Cliquer pour ouvrir",
|
||||||
"com_ui_artifacts": "Artefacts",
|
"com_ui_artifacts": "Artefacts",
|
||||||
"com_ui_artifacts_options": "Options des Artefacts",
|
"com_ui_artifacts_options": "Options des Artefacts",
|
||||||
"com_ui_artifacts_toggle": "Afficher/Masquer l'interface des artefacts",
|
"com_ui_artifacts_toggle": "Afficher/Masquer l'interface des artefacts",
|
||||||
|
|
|
||||||
|
|
@ -676,7 +676,7 @@
|
||||||
"com_ui_archive": "לארכיון",
|
"com_ui_archive": "לארכיון",
|
||||||
"com_ui_archive_delete_error": "מחיקת השיחה מהארכיון נכשלה",
|
"com_ui_archive_delete_error": "מחיקת השיחה מהארכיון נכשלה",
|
||||||
"com_ui_archive_error": "אירעה שגיאה באירכוב השיחה",
|
"com_ui_archive_error": "אירעה שגיאה באירכוב השיחה",
|
||||||
"com_ui_artifact_click": "לחץ לפתיחה",
|
"com_ui_click_to_open": "לחץ לפתיחה",
|
||||||
"com_ui_artifacts": "רכיבי תצוגה",
|
"com_ui_artifacts": "רכיבי תצוגה",
|
||||||
"com_ui_artifacts_options": "אפשרויות ארטיפקטים",
|
"com_ui_artifacts_options": "אפשרויות ארטיפקטים",
|
||||||
"com_ui_artifacts_toggle": "הפעל/כבה רכיבי תצוגה",
|
"com_ui_artifacts_toggle": "הפעל/כבה רכיבי תצוגה",
|
||||||
|
|
|
||||||
|
|
@ -479,7 +479,7 @@
|
||||||
"com_ui_api_key": "API kulcs",
|
"com_ui_api_key": "API kulcs",
|
||||||
"com_ui_archive": "Archiválás",
|
"com_ui_archive": "Archiválás",
|
||||||
"com_ui_archive_error": "Nem sikerült archiválni a beszélgetést",
|
"com_ui_archive_error": "Nem sikerült archiválni a beszélgetést",
|
||||||
"com_ui_artifact_click": "Kattintson a megnyitáshoz",
|
"com_ui_click_to_open": "Kattintson a megnyitáshoz",
|
||||||
"com_ui_artifacts": "Műtermékek",
|
"com_ui_artifacts": "Műtermékek",
|
||||||
"com_ui_artifacts_toggle": "Műtermék kezelőfelület váltása",
|
"com_ui_artifacts_toggle": "Műtermék kezelőfelület váltása",
|
||||||
"com_ui_artifacts_toggle_agent": "Műtermékek engedélyezése",
|
"com_ui_artifacts_toggle_agent": "Műtermékek engedélyezése",
|
||||||
|
|
|
||||||
|
|
@ -541,7 +541,7 @@
|
||||||
"com_ui_api_key": "Chiave API",
|
"com_ui_api_key": "Chiave API",
|
||||||
"com_ui_archive": "Archivia",
|
"com_ui_archive": "Archivia",
|
||||||
"com_ui_archive_error": "Errore durante l'archiviazione della conversazione",
|
"com_ui_archive_error": "Errore durante l'archiviazione della conversazione",
|
||||||
"com_ui_artifact_click": "Clicca per aprire",
|
"com_ui_click_to_open": "Clicca per aprire",
|
||||||
"com_ui_artifacts": "Artefatti",
|
"com_ui_artifacts": "Artefatti",
|
||||||
"com_ui_artifacts_toggle": "Mostra/Nascondi Interfaccia Artefatti",
|
"com_ui_artifacts_toggle": "Mostra/Nascondi Interfaccia Artefatti",
|
||||||
"com_ui_artifacts_toggle_agent": "Abilita artefatti",
|
"com_ui_artifacts_toggle_agent": "Abilita artefatti",
|
||||||
|
|
|
||||||
|
|
@ -576,7 +576,7 @@
|
||||||
"com_ui_archive": "アーカイブ",
|
"com_ui_archive": "アーカイブ",
|
||||||
"com_ui_archive_delete_error": "アーカイブされた会話の削除に失敗しました",
|
"com_ui_archive_delete_error": "アーカイブされた会話の削除に失敗しました",
|
||||||
"com_ui_archive_error": "アーカイブに失敗しました。",
|
"com_ui_archive_error": "アーカイブに失敗しました。",
|
||||||
"com_ui_artifact_click": "クリックして開く",
|
"com_ui_click_to_open": "クリックして開く",
|
||||||
"com_ui_artifacts": "アーティファクト",
|
"com_ui_artifacts": "アーティファクト",
|
||||||
"com_ui_artifacts_options": "アーティファクト・オプション",
|
"com_ui_artifacts_options": "アーティファクト・オプション",
|
||||||
"com_ui_artifacts_toggle": "アーティファクト UI の切替",
|
"com_ui_artifacts_toggle": "アーティファクト UI の切替",
|
||||||
|
|
|
||||||
|
|
@ -567,7 +567,7 @@
|
||||||
"com_ui_archive": "아카이브",
|
"com_ui_archive": "아카이브",
|
||||||
"com_ui_archive_delete_error": "저장된 대화 삭제 실패",
|
"com_ui_archive_delete_error": "저장된 대화 삭제 실패",
|
||||||
"com_ui_archive_error": "대화 아카이브 실패",
|
"com_ui_archive_error": "대화 아카이브 실패",
|
||||||
"com_ui_artifact_click": "클릭하여 열기",
|
"com_ui_click_to_open": "클릭하여 열기",
|
||||||
"com_ui_artifacts": "아티팩트",
|
"com_ui_artifacts": "아티팩트",
|
||||||
"com_ui_artifacts_options": "아티팩트 옵션",
|
"com_ui_artifacts_options": "아티팩트 옵션",
|
||||||
"com_ui_artifacts_toggle": "아티팩트 UI 표시/숨기기",
|
"com_ui_artifacts_toggle": "아티팩트 UI 표시/숨기기",
|
||||||
|
|
|
||||||
|
|
@ -688,7 +688,7 @@
|
||||||
"com_ui_archive": "Arhīvs",
|
"com_ui_archive": "Arhīvs",
|
||||||
"com_ui_archive_delete_error": "Neizdevās izdzēst arhivēto sarunu.",
|
"com_ui_archive_delete_error": "Neizdevās izdzēst arhivēto sarunu.",
|
||||||
"com_ui_archive_error": "Neizdevās arhivēt sarunu.",
|
"com_ui_archive_error": "Neizdevās arhivēt sarunu.",
|
||||||
"com_ui_artifact_click": "Noklikšķiniet, lai atvērtu",
|
"com_ui_click_to_open": "Noklikšķiniet, lai atvērtu",
|
||||||
"com_ui_artifacts": "Artefakti",
|
"com_ui_artifacts": "Artefakti",
|
||||||
"com_ui_artifacts_options": "Artefaktu opcijas",
|
"com_ui_artifacts_options": "Artefaktu opcijas",
|
||||||
"com_ui_artifacts_toggle": "Pārslēgt artefaktu lietotāja saskarni",
|
"com_ui_artifacts_toggle": "Pārslēgt artefaktu lietotāja saskarni",
|
||||||
|
|
|
||||||
|
|
@ -683,7 +683,7 @@
|
||||||
"com_ui_archive": "Arkiver",
|
"com_ui_archive": "Arkiver",
|
||||||
"com_ui_archive_delete_error": "Sletting av arkivert samtale mislyktes.",
|
"com_ui_archive_delete_error": "Sletting av arkivert samtale mislyktes.",
|
||||||
"com_ui_archive_error": "Arkivering av samtale mislyktes.",
|
"com_ui_archive_error": "Arkivering av samtale mislyktes.",
|
||||||
"com_ui_artifact_click": "Klikk for å åpne",
|
"com_ui_click_to_open": "Klikk for å åpne",
|
||||||
"com_ui_artifacts": "Artefakter",
|
"com_ui_artifacts": "Artefakter",
|
||||||
"com_ui_artifacts_options": "Alternativer for artefakter",
|
"com_ui_artifacts_options": "Alternativer for artefakter",
|
||||||
"com_ui_artifacts_toggle": "Veksle artefakt-UI",
|
"com_ui_artifacts_toggle": "Veksle artefakt-UI",
|
||||||
|
|
|
||||||
|
|
@ -429,7 +429,7 @@
|
||||||
"com_ui_all_proper": "Wszystkie",
|
"com_ui_all_proper": "Wszystkie",
|
||||||
"com_ui_archive": "Archiwum",
|
"com_ui_archive": "Archiwum",
|
||||||
"com_ui_archive_error": "Nie udało się archiwizować rozmowy",
|
"com_ui_archive_error": "Nie udało się archiwizować rozmowy",
|
||||||
"com_ui_artifact_click": "Kliknij, aby otworzyć",
|
"com_ui_click_to_open": "Kliknij, aby otworzyć",
|
||||||
"com_ui_artifacts": "Artefakty",
|
"com_ui_artifacts": "Artefakty",
|
||||||
"com_ui_artifacts_toggle": "Przełącz interfejs artefaktów",
|
"com_ui_artifacts_toggle": "Przełącz interfejs artefaktów",
|
||||||
"com_ui_ascending": "Rosnąco",
|
"com_ui_ascending": "Rosnąco",
|
||||||
|
|
|
||||||
|
|
@ -582,7 +582,7 @@
|
||||||
"com_ui_api_key": "Chave API",
|
"com_ui_api_key": "Chave API",
|
||||||
"com_ui_archive": "Arquivar",
|
"com_ui_archive": "Arquivar",
|
||||||
"com_ui_archive_error": "Falha ao arquivar conversa",
|
"com_ui_archive_error": "Falha ao arquivar conversa",
|
||||||
"com_ui_artifact_click": "Clique para abrir",
|
"com_ui_click_to_open": "Clique para abrir",
|
||||||
"com_ui_artifacts": "Artefatos",
|
"com_ui_artifacts": "Artefatos",
|
||||||
"com_ui_artifacts_toggle": "Alternar UI de Artefatos",
|
"com_ui_artifacts_toggle": "Alternar UI de Artefatos",
|
||||||
"com_ui_artifacts_toggle_agent": "Habilitar artefatos",
|
"com_ui_artifacts_toggle_agent": "Habilitar artefatos",
|
||||||
|
|
|
||||||
|
|
@ -656,7 +656,7 @@
|
||||||
"com_ui_archive": "Arquivar",
|
"com_ui_archive": "Arquivar",
|
||||||
"com_ui_archive_delete_error": "Falha ao eliminar conversa arquivada",
|
"com_ui_archive_delete_error": "Falha ao eliminar conversa arquivada",
|
||||||
"com_ui_archive_error": "Falha ao arquivar conversa",
|
"com_ui_archive_error": "Falha ao arquivar conversa",
|
||||||
"com_ui_artifact_click": "Clique para abrir",
|
"com_ui_click_to_open": "Clique para abrir",
|
||||||
"com_ui_artifacts": "Artefatos",
|
"com_ui_artifacts": "Artefatos",
|
||||||
"com_ui_artifacts_options": "Opções de Artefactos",
|
"com_ui_artifacts_options": "Opções de Artefactos",
|
||||||
"com_ui_artifacts_toggle": "Alternar UI de Artefatos",
|
"com_ui_artifacts_toggle": "Alternar UI de Artefatos",
|
||||||
|
|
|
||||||
|
|
@ -682,7 +682,7 @@
|
||||||
"com_ui_archive": "Архивировать",
|
"com_ui_archive": "Архивировать",
|
||||||
"com_ui_archive_delete_error": "Не удалось удалить архивированный чат",
|
"com_ui_archive_delete_error": "Не удалось удалить архивированный чат",
|
||||||
"com_ui_archive_error": "Не удалось заархивировать чат",
|
"com_ui_archive_error": "Не удалось заархивировать чат",
|
||||||
"com_ui_artifact_click": "Нажмите, чтобы открыть",
|
"com_ui_click_to_open": "Нажмите, чтобы открыть",
|
||||||
"com_ui_artifacts": "Артефакты",
|
"com_ui_artifacts": "Артефакты",
|
||||||
"com_ui_artifacts_options": "Параметры артефактов",
|
"com_ui_artifacts_options": "Параметры артефактов",
|
||||||
"com_ui_artifacts_toggle": "Показать/скрыть артефакты",
|
"com_ui_artifacts_toggle": "Показать/скрыть артефакты",
|
||||||
|
|
|
||||||
|
|
@ -477,7 +477,7 @@
|
||||||
"com_ui_api_key": "คีย์ API",
|
"com_ui_api_key": "คีย์ API",
|
||||||
"com_ui_archive": "เก็บถาวร",
|
"com_ui_archive": "เก็บถาวร",
|
||||||
"com_ui_archive_error": "ไม่สามารถเก็บถาวรการสนทนา",
|
"com_ui_archive_error": "ไม่สามารถเก็บถาวรการสนทนา",
|
||||||
"com_ui_artifact_click": "คลิกเพื่อเปิด",
|
"com_ui_click_to_open": "คลิกเพื่อเปิด",
|
||||||
"com_ui_artifacts": "สิ่งประดิษฐ์",
|
"com_ui_artifacts": "สิ่งประดิษฐ์",
|
||||||
"com_ui_artifacts_toggle": "สลับ UI สิ่งประดิษฐ์",
|
"com_ui_artifacts_toggle": "สลับ UI สิ่งประดิษฐ์",
|
||||||
"com_ui_artifacts_toggle_agent": "เปิดใช้งานสิ่งประดิษฐ์",
|
"com_ui_artifacts_toggle_agent": "เปิดใช้งานสิ่งประดิษฐ์",
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,7 @@
|
||||||
"com_ui_all_proper": "Tümü",
|
"com_ui_all_proper": "Tümü",
|
||||||
"com_ui_archive": "Arşivle",
|
"com_ui_archive": "Arşivle",
|
||||||
"com_ui_archive_error": "Konuşmayı arşivleyemedi",
|
"com_ui_archive_error": "Konuşmayı arşivleyemedi",
|
||||||
"com_ui_artifact_click": "Açmak için tıklayın",
|
"com_ui_click_to_open": "Açmak için tıklayın",
|
||||||
"com_ui_artifacts": "Yapıtlar",
|
"com_ui_artifacts": "Yapıtlar",
|
||||||
"com_ui_artifacts_toggle": "Yapıtlar Arayüzünü Aç/Kapat",
|
"com_ui_artifacts_toggle": "Yapıtlar Arayüzünü Aç/Kapat",
|
||||||
"com_ui_ascending": "Artan",
|
"com_ui_ascending": "Artan",
|
||||||
|
|
|
||||||
|
|
@ -683,7 +683,7 @@
|
||||||
"com_ui_archive": "Архівувати",
|
"com_ui_archive": "Архівувати",
|
||||||
"com_ui_archive_delete_error": "Не вдалося видалити заархівований чат",
|
"com_ui_archive_delete_error": "Не вдалося видалити заархівований чат",
|
||||||
"com_ui_archive_error": "Не вдалося заархівувати чат",
|
"com_ui_archive_error": "Не вдалося заархівувати чат",
|
||||||
"com_ui_artifact_click": "Натисніть, щоб відкрити",
|
"com_ui_click_to_open": "Натисніть, щоб відкрити",
|
||||||
"com_ui_artifacts": "Артефакти",
|
"com_ui_artifacts": "Артефакти",
|
||||||
"com_ui_artifacts_options": "Параметри артефактів",
|
"com_ui_artifacts_options": "Параметри артефактів",
|
||||||
"com_ui_artifacts_toggle": "Показати/приховати артефакти",
|
"com_ui_artifacts_toggle": "Показати/приховати артефакти",
|
||||||
|
|
|
||||||
|
|
@ -686,7 +686,7 @@
|
||||||
"com_ui_archive": "归档",
|
"com_ui_archive": "归档",
|
||||||
"com_ui_archive_delete_error": "删除已归档对话失败",
|
"com_ui_archive_delete_error": "删除已归档对话失败",
|
||||||
"com_ui_archive_error": "归档对话失败",
|
"com_ui_archive_error": "归档对话失败",
|
||||||
"com_ui_artifact_click": "点击以打开",
|
"com_ui_click_to_open": "点击以打开",
|
||||||
"com_ui_artifacts": "Artifacts",
|
"com_ui_artifacts": "Artifacts",
|
||||||
"com_ui_artifacts_options": "Artifacts 选项",
|
"com_ui_artifacts_options": "Artifacts 选项",
|
||||||
"com_ui_artifacts_toggle": "切换 Artifacts UI",
|
"com_ui_artifacts_toggle": "切换 Artifacts UI",
|
||||||
|
|
|
||||||
|
|
@ -548,7 +548,7 @@
|
||||||
"com_ui_api_key": "API 金鑰",
|
"com_ui_api_key": "API 金鑰",
|
||||||
"com_ui_archive": "封存",
|
"com_ui_archive": "封存",
|
||||||
"com_ui_archive_error": "封存對話時發生錯誤",
|
"com_ui_archive_error": "封存對話時發生錯誤",
|
||||||
"com_ui_artifact_click": "點擊開啟",
|
"com_ui_click_to_open": "點擊開啟",
|
||||||
"com_ui_artifacts": "成品",
|
"com_ui_artifacts": "成品",
|
||||||
"com_ui_artifacts_toggle": "切換成品介面",
|
"com_ui_artifacts_toggle": "切換成品介面",
|
||||||
"com_ui_ascending": "遞增",
|
"com_ui_ascending": "遞增",
|
||||||
|
|
|
||||||
|
|
@ -2715,6 +2715,8 @@ html {
|
||||||
.animate-pulse-slow {
|
.animate-pulse-slow {
|
||||||
animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
animation: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* iOS-inspired smooth animations */
|
||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
@ -2730,10 +2732,45 @@ html {
|
||||||
animation: fadeIn 0.5s ease-out forwards;
|
animation: fadeIn 0.5s ease-out forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Enhanced smooth scaling for interactions */
|
||||||
.scale-98 {
|
.scale-98 {
|
||||||
transform: scale(0.98);
|
transform: scale(0.98);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add hardware acceleration for smoother animations */
|
||||||
|
.will-change-transform {
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.will-change-opacity {
|
||||||
|
will-change: opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prevent content flash and layout shifts in artifacts */
|
||||||
|
[data-radix-scroll-area-viewport] {
|
||||||
|
/* Prevent scrollbar from causing layout shifts */
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure smooth tab content transitions */
|
||||||
|
[role="tabpanel"] {
|
||||||
|
/* Use GPU acceleration for tab content */
|
||||||
|
transform: translateZ(0);
|
||||||
|
backface-visibility: hidden;
|
||||||
|
-webkit-font-smoothing: subpixel-antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prevent flash of content during mounting */
|
||||||
|
[role="tabpanel"][data-state="inactive"] {
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[role="tabpanel"][data-state="active"] {
|
||||||
|
visibility: visible;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
/* Chat Badges Animation */
|
/* Chat Badges Animation */
|
||||||
|
|
||||||
@keyframes ios-wiggle {
|
@keyframes ios-wiggle {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,69 @@ module.exports = {
|
||||||
'0%': { transform: 'translateX(0)' },
|
'0%': { transform: 'translateX(0)' },
|
||||||
'100%': { transform: 'translateX(100%)' },
|
'100%': { transform: 'translateX(100%)' },
|
||||||
},
|
},
|
||||||
|
// iOS-inspired smooth animations
|
||||||
|
'artifact-slide-up': {
|
||||||
|
'0%': {
|
||||||
|
transform: 'translateY(100%) scale(0.95)',
|
||||||
|
opacity: '0',
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
transform: 'translateY(0) scale(1)',
|
||||||
|
opacity: '1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'artifact-slide-down': {
|
||||||
|
'0%': {
|
||||||
|
transform: 'translateY(0) scale(1)',
|
||||||
|
opacity: '1',
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
transform: 'translateY(100%) scale(0.95)',
|
||||||
|
opacity: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'artifact-slide-in-desktop': {
|
||||||
|
'0%': {
|
||||||
|
transform: 'translateX(20px)',
|
||||||
|
opacity: '0',
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
transform: 'translateX(0)',
|
||||||
|
opacity: '1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'artifact-slide-out-desktop': {
|
||||||
|
'0%': {
|
||||||
|
transform: 'translateX(0)',
|
||||||
|
opacity: '1',
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
transform: 'translateX(20px)',
|
||||||
|
opacity: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'backdrop-fade-in': {
|
||||||
|
'0%': { opacity: '0' },
|
||||||
|
'100%': { opacity: '1' },
|
||||||
|
},
|
||||||
|
'backdrop-fade-out': {
|
||||||
|
'0%': { opacity: '1' },
|
||||||
|
'100%': { opacity: '0' },
|
||||||
|
},
|
||||||
|
'tab-slide': {
|
||||||
|
'0%': { transform: 'translateX(var(--tab-slide-from))' },
|
||||||
|
'100%': { transform: 'translateX(var(--tab-slide-to))' },
|
||||||
|
},
|
||||||
|
'thinking-appear': {
|
||||||
|
'0%': {
|
||||||
|
opacity: '0',
|
||||||
|
transform: 'scale(0.9) translateY(4px)',
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
opacity: '1',
|
||||||
|
transform: 'scale(1) translateY(0)',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
'fade-in': 'fadeIn 0.5s ease-out forwards',
|
'fade-in': 'fadeIn 0.5s ease-out forwards',
|
||||||
|
|
@ -56,6 +119,22 @@ module.exports = {
|
||||||
'slide-in-left': 'slide-in-left 300ms cubic-bezier(0.25, 0.1, 0.25, 1)',
|
'slide-in-left': 'slide-in-left 300ms cubic-bezier(0.25, 0.1, 0.25, 1)',
|
||||||
'slide-out-left': 'slide-out-left 300ms cubic-bezier(0.25, 0.1, 0.25, 1)',
|
'slide-out-left': 'slide-out-left 300ms cubic-bezier(0.25, 0.1, 0.25, 1)',
|
||||||
'slide-out-right': 'slide-out-right 300ms cubic-bezier(0.25, 0.1, 0.25, 1)',
|
'slide-out-right': 'slide-out-right 300ms cubic-bezier(0.25, 0.1, 0.25, 1)',
|
||||||
|
// iOS-inspired smooth animations
|
||||||
|
'artifact-slide-up': 'artifact-slide-up 0.45s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||||
|
'artifact-slide-down': 'artifact-slide-down 0.35s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||||
|
'artifact-slide-in-desktop':
|
||||||
|
'artifact-slide-in-desktop 0.5s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||||
|
'artifact-slide-out-desktop':
|
||||||
|
'artifact-slide-out-desktop 0.35s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||||
|
'backdrop-fade-in': 'backdrop-fade-in 0.3s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||||
|
'backdrop-fade-out': 'backdrop-fade-out 0.25s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||||
|
'tab-slide': 'tab-slide 0.35s cubic-bezier(0.32, 0.72, 0, 1)',
|
||||||
|
'thinking-appear': 'thinking-appear 0.4s cubic-bezier(0.34, 1.56, 0.64, 1)',
|
||||||
|
},
|
||||||
|
transitionTimingFunction: {
|
||||||
|
ios: 'cubic-bezier(0.32, 0.72, 0, 1)',
|
||||||
|
'ios-spring': 'cubic-bezier(0.34, 1.56, 0.64, 1)',
|
||||||
|
'ios-decelerate': 'cubic-bezier(0, 0, 0.2, 1)',
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
gray: {
|
gray: {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue