✨enhancement: add setting chat width container (#5289)
* ✨enhancement: add setting conversation width * ✨enahncement: cleanup log and change improve accesibility * ✨enahcement: move const beta version
This commit is contained in:
parent
bea806c26c
commit
da2f97c227
13
web-app/src/components/ui/skeleton.tsx
Normal file
13
web-app/src/components/ui/skeleton.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
function Skeleton({ className, ...props }: React.ComponentProps<'div'>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="skeleton"
|
||||||
|
className={cn('bg-main-view-fg/10', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Skeleton }
|
||||||
@ -21,7 +21,6 @@ import {
|
|||||||
IconTool,
|
IconTool,
|
||||||
IconCodeCircle2,
|
IconCodeCircle2,
|
||||||
IconPlayerStopFilled,
|
IconPlayerStopFilled,
|
||||||
IconBrandSpeedtest,
|
|
||||||
IconX,
|
IconX,
|
||||||
} from '@tabler/icons-react'
|
} from '@tabler/icons-react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -45,12 +44,7 @@ type ChatInputProps = {
|
|||||||
initialMessage?: boolean
|
initialMessage?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChatInput = ({
|
const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
||||||
model,
|
|
||||||
className,
|
|
||||||
showSpeedToken = true,
|
|
||||||
initialMessage,
|
|
||||||
}: ChatInputProps) => {
|
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
||||||
const [isFocused, setIsFocused] = useState(false)
|
const [isFocused, setIsFocused] = useState(false)
|
||||||
const [rows, setRows] = useState(1)
|
const [rows, setRows] = useState(1)
|
||||||
@ -60,7 +54,7 @@ const ChatInput = ({
|
|||||||
const { currentThreadId } = useThreads()
|
const { currentThreadId } = useThreads()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { spellCheckChatInput } = useGeneralSetting()
|
const { spellCheckChatInput } = useGeneralSetting()
|
||||||
const { tokenSpeed } = useAppState()
|
|
||||||
const { showModal, PromiseModal: OutOfContextModal } =
|
const { showModal, PromiseModal: OutOfContextModal } =
|
||||||
useOutOfContextPromiseModal()
|
useOutOfContextPromiseModal()
|
||||||
const maxRows = 10
|
const maxRows = 10
|
||||||
@ -559,15 +553,6 @@ const ChatInput = ({
|
|||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showSpeedToken && (
|
|
||||||
<div className="flex items-center gap-1 text-main-view-fg/60 text-xs">
|
|
||||||
<IconBrandSpeedtest size={18} />
|
|
||||||
<span>
|
|
||||||
{Math.round(tokenSpeed?.tokenSpeed ?? 0)} tokens/sec
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{streamingContent ? (
|
{streamingContent ? (
|
||||||
|
|||||||
61
web-app/src/containers/ChatWidthSwitcher.tsx
Normal file
61
web-app/src/containers/ChatWidthSwitcher.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
|
import { useAppearance } from '@/hooks/useAppearance'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
import { IconCircleCheckFilled } from '@tabler/icons-react'
|
||||||
|
|
||||||
|
export function ChatWidthSwitcher() {
|
||||||
|
const { chatWidth, setChatWidth } = useAppearance()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<button
|
||||||
|
className={cn(
|
||||||
|
'w-full overflow-hidden border border-main-view-fg/10 rounded-md my-2 pb-2 cursor-pointer',
|
||||||
|
chatWidth === 'compact' && 'border-accent'
|
||||||
|
)}
|
||||||
|
onClick={() => setChatWidth('compact')}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between px-4 py-2 bg-main-view-fg/10">
|
||||||
|
<span className="font-medium text-xs font-sans">Compact Width</span>
|
||||||
|
{chatWidth === 'compact' && (
|
||||||
|
<IconCircleCheckFilled className="size-4 text-accent" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="overflow-auto p-2">
|
||||||
|
<div className="flex flex-col px-10 gap-2 mt-2">
|
||||||
|
<Skeleton className="h-2 w-full rounded-full" />
|
||||||
|
<Skeleton className="h-2 w-full rounded-full" />
|
||||||
|
<Skeleton className="h-2 w-full rounded-full" />
|
||||||
|
<div className="bg-main-view-fg/10 h-8 px-4 w-full flex-shrink-0 border-none resize-none outline-0 rounded-2xl flex items-center">
|
||||||
|
<span className="text-main-view-fg/50">Ask me anything...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={cn(
|
||||||
|
'w-full overflow-hidden border border-main-view-fg/10 rounded-md my-2 pb-2 cursor-pointer',
|
||||||
|
chatWidth === 'full' && 'border-accent'
|
||||||
|
)}
|
||||||
|
onClick={() => setChatWidth('full')}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between px-4 py-2 bg-main-view-fg/10">
|
||||||
|
<span className="font-medium text-xs font-sans">Full Width</span>
|
||||||
|
{chatWidth === 'full' && (
|
||||||
|
<IconCircleCheckFilled className="size-4 text-accent" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="overflow-auto p-2">
|
||||||
|
<div className="flex flex-col gap-2 mt-2">
|
||||||
|
<Skeleton className="h-2 w-full rounded-full" />
|
||||||
|
<Skeleton className="h-2 w-full rounded-full" />
|
||||||
|
<Skeleton className="h-2 w-full rounded-full" />
|
||||||
|
<div className="bg-main-view-fg/10 h-8 px-4 w-full flex-shrink-0 border-none resize-none outline-0 rounded-2xl flex items-center">
|
||||||
|
<span className="text-main-view-fg/50">Ask me anything...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -359,12 +359,11 @@ export const ThreadContent = memo(
|
|||||||
|
|
||||||
{!isToolCalls && (
|
{!isToolCalls && (
|
||||||
<div className="flex items-center gap-2 mt-2 text-main-view-fg/60 text-xs">
|
<div className="flex items-center gap-2 mt-2 text-main-view-fg/60 text-xs">
|
||||||
|
<div className={cn('flex items-center gap-2')}>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center gap-2',
|
'flex items-center gap-2',
|
||||||
item.isLastMessage &&
|
item.isLastMessage && streamingContent && 'hidden'
|
||||||
streamingContent &&
|
|
||||||
'opacity-0 visibility-hidden pointer-events-none'
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CopyButton text={item.content?.[0]?.text.value || ''} />
|
<CopyButton text={item.content?.[0]?.text.value || ''} />
|
||||||
@ -448,8 +447,10 @@ export const ThreadContent = memo(
|
|||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<TokenSpeedIndicator
|
<TokenSpeedIndicator
|
||||||
|
streaming={Boolean(item.isLastMessage && streamingContent)}
|
||||||
metadata={item.metadata}
|
metadata={item.metadata}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,19 +1,28 @@
|
|||||||
import { IconBrandSpeedtest } from '@tabler/icons-react'
|
import { useAppState } from '@/hooks/useAppState'
|
||||||
|
import { Gauge } from 'lucide-react'
|
||||||
|
|
||||||
interface TokenSpeedIndicatorProps {
|
interface TokenSpeedIndicatorProps {
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
|
streaming?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TokenSpeedIndicator = ({
|
export const TokenSpeedIndicator = ({
|
||||||
metadata
|
metadata,
|
||||||
|
streaming,
|
||||||
}: TokenSpeedIndicatorProps) => {
|
}: TokenSpeedIndicatorProps) => {
|
||||||
const persistedTokenSpeed = (metadata?.tokenSpeed as { tokenSpeed: number })?.tokenSpeed
|
const { tokenSpeed } = useAppState()
|
||||||
|
const persistedTokenSpeed = (metadata?.tokenSpeed as { tokenSpeed: number })
|
||||||
|
?.tokenSpeed
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-1 text-main-view-fg/60 text-xs">
|
<div className="flex items-center gap-1 text-main-view-fg/60 text-xs">
|
||||||
<IconBrandSpeedtest size={16} />
|
<Gauge size={16} />
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
{Math.round(persistedTokenSpeed)} tokens/sec
|
{Math.round(
|
||||||
|
streaming ? Number(tokenSpeed?.tokenSpeed) : persistedTokenSpeed
|
||||||
|
)}
|
||||||
|
tokens/sec
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -22,8 +22,8 @@ const DialogAppUpdater = () => {
|
|||||||
setRemindMeLater(true)
|
setRemindMeLater(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const beta = VERSION.includes('beta')
|
|
||||||
const nightly = VERSION.includes('-')
|
const nightly = VERSION.includes('-')
|
||||||
|
const beta = VERSION.includes('beta')
|
||||||
|
|
||||||
const { release, fetchLatestRelease } = useReleaseNotes()
|
const { release, fetchLatestRelease } = useReleaseNotes()
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,10 @@ import { rgb, oklch, formatCss } from 'culori'
|
|||||||
import { useTheme } from './useTheme'
|
import { useTheme } from './useTheme'
|
||||||
|
|
||||||
export type FontSize = '14px' | '15px' | '16px' | '18px'
|
export type FontSize = '14px' | '15px' | '16px' | '18px'
|
||||||
|
export type ChatWidth = 'full' | 'compact'
|
||||||
|
|
||||||
interface AppearanceState {
|
interface AppearanceState {
|
||||||
|
chatWidth: ChatWidth
|
||||||
fontSize: FontSize
|
fontSize: FontSize
|
||||||
appBgColor: RgbaColor
|
appBgColor: RgbaColor
|
||||||
appMainViewBgColor: RgbaColor
|
appMainViewBgColor: RgbaColor
|
||||||
@ -19,6 +21,7 @@ interface AppearanceState {
|
|||||||
appAccentTextColor: string
|
appAccentTextColor: string
|
||||||
appDestructiveTextColor: string
|
appDestructiveTextColor: string
|
||||||
appLeftPanelTextColor: string
|
appLeftPanelTextColor: string
|
||||||
|
setChatWidth: (size: ChatWidth) => void
|
||||||
setFontSize: (size: FontSize) => void
|
setFontSize: (size: FontSize) => void
|
||||||
setAppBgColor: (color: RgbaColor) => void
|
setAppBgColor: (color: RgbaColor) => void
|
||||||
setAppMainViewBgColor: (color: RgbaColor) => void
|
setAppMainViewBgColor: (color: RgbaColor) => void
|
||||||
@ -129,6 +132,7 @@ export const useAppearance = create<AppearanceState>()(
|
|||||||
persist(
|
persist(
|
||||||
(set) => {
|
(set) => {
|
||||||
return {
|
return {
|
||||||
|
chatWidth: 'compact',
|
||||||
fontSize: defaultFontSize,
|
fontSize: defaultFontSize,
|
||||||
appBgColor: defaultAppBgColor,
|
appBgColor: defaultAppBgColor,
|
||||||
appMainViewBgColor: defaultAppMainViewBgColor,
|
appMainViewBgColor: defaultAppMainViewBgColor,
|
||||||
@ -270,6 +274,10 @@ export const useAppearance = create<AppearanceState>()(
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setChatWidth: (value: ChatWidth) => {
|
||||||
|
set({ chatWidth: value })
|
||||||
|
},
|
||||||
|
|
||||||
setFontSize: (size: FontSize) => {
|
setFontSize: (size: FontSize) => {
|
||||||
// Update CSS variable
|
// Update CSS variable
|
||||||
document.documentElement.style.setProperty('--font-size-base', size)
|
document.documentElement.style.setProperty('--font-size-base', size)
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import CodeBlockStyleSwitcher from '@/containers/CodeBlockStyleSwitcher'
|
|||||||
import { LineNumbersSwitcher } from '@/containers/LineNumbersSwitcher'
|
import { LineNumbersSwitcher } from '@/containers/LineNumbersSwitcher'
|
||||||
import { CodeBlockExample } from '@/containers/CodeBlockExample'
|
import { CodeBlockExample } from '@/containers/CodeBlockExample'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
import { ChatWidthSwitcher } from '@/containers/ChatWidthSwitcher'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const Route = createFileRoute(route.settings.appearance as any)({
|
export const Route = createFileRoute(route.settings.appearance as any)({
|
||||||
@ -98,6 +99,15 @@ function Appareances() {
|
|||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* Chat Message */}
|
||||||
|
<Card>
|
||||||
|
<CardItem
|
||||||
|
title="Chat Width"
|
||||||
|
description="Choose the width of the chat area to customize your conversation view."
|
||||||
|
/>
|
||||||
|
<ChatWidthSwitcher />
|
||||||
|
</Card>
|
||||||
|
|
||||||
{/* Codeblock */}
|
{/* Codeblock */}
|
||||||
<Card>
|
<Card>
|
||||||
<CardItem
|
<CardItem
|
||||||
|
|||||||
@ -35,7 +35,7 @@ function ThreadDetail() {
|
|||||||
const { setCurrentAssistant, assistants } = useAssistant()
|
const { setCurrentAssistant, assistants } = useAssistant()
|
||||||
const { setMessages } = useMessages()
|
const { setMessages } = useMessages()
|
||||||
const { streamingContent } = useAppState()
|
const { streamingContent } = useAppState()
|
||||||
const { appMainViewBgColor } = useAppearance()
|
const { appMainViewBgColor, chatWidth } = useAppearance()
|
||||||
|
|
||||||
const { messages } = useMessages(
|
const { messages } = useMessages(
|
||||||
useShallow((state) => ({
|
useShallow((state) => ({
|
||||||
@ -213,7 +213,12 @@ function ThreadDetail() {
|
|||||||
'flex flex-col h-full w-full overflow-auto px-4 pt-4 pb-3'
|
'flex flex-col h-full w-full overflow-auto px-4 pt-4 pb-3'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="w-4/6 mx-auto flex max-w-full flex-col grow">
|
<div
|
||||||
|
className={cn(
|
||||||
|
'w-4/6 mx-auto flex max-w-full flex-col grow',
|
||||||
|
chatWidth === 'compact' ? 'w-4/6' : 'w-full'
|
||||||
|
)}
|
||||||
|
>
|
||||||
{messages &&
|
{messages &&
|
||||||
messages.map((item, index) => {
|
messages.map((item, index) => {
|
||||||
// Only pass isLastMessage to the last message in the array
|
// Only pass isLastMessage to the last message in the array
|
||||||
@ -247,7 +252,12 @@ function ThreadDetail() {
|
|||||||
<StreamingContent threadId={threadId} />
|
<StreamingContent threadId={threadId} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-4/6 mx-auto pt-2 pb-3 shrink-0 relative">
|
<div
|
||||||
|
className={cn(
|
||||||
|
' mx-auto pt-2 pb-3 shrink-0 relative',
|
||||||
|
chatWidth === 'compact' ? 'w-4/6' : 'w-full px-3'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'absolute z-0 -top-6 h-8 py-1 flex w-full justify-center pointer-events-none opacity-0 visibility-hidden',
|
'absolute z-0 -top-6 h-8 py-1 flex w-full justify-center pointer-events-none opacity-0 visibility-hidden',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user