diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index 3161feeb..683f8269 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -23,6 +23,14 @@ export class WindowManager { // Track if mouse events are forcibly ignored private forceIgnoreMouse = false; + private getDefaultWindowSize(): { width: number; height: number } { + const { width, height } = screen.getPrimaryDisplay().workArea; + return { + width: Math.round(width * 0.5), + height: Math.round(height * 0.6), + }; + } + constructor() { ipcMain.on('renderer-ready-for-mode-change', (_event, newMode) => { if (newMode === 'pet') { @@ -54,9 +62,10 @@ export class WindowManager { } createWindow(options: Electron.BrowserWindowConstructorOptions): BrowserWindow { + const { width, height } = this.getDefaultWindowSize(); this.window = new BrowserWindow({ - width: 900, - height: 670, + width, + height, show: false, transparent: true, backgroundColor: '#ffffff', @@ -168,7 +177,8 @@ export class WindowManager { if (this.windowedBounds) { this.window.setBounds(this.windowedBounds); } else { - this.window.setSize(900, 670); + const { width, height } = this.getDefaultWindowSize(); + this.window.setSize(width, height); this.window.center(); } diff --git a/src/renderer/src/components/footer/footer-styles.tsx b/src/renderer/src/components/footer/footer-styles.tsx index 49769a18..5f3995d9 100644 --- a/src/renderer/src/components/footer/footer-styles.tsx +++ b/src/renderer/src/components/footer/footer-styles.tsx @@ -1,7 +1,7 @@ import { SystemStyleObject } from '@chakra-ui/react'; interface FooterStyles { - container: (isCollapsed: boolean) => SystemStyleObject + container: (isCollapsed: boolean, hasAttachments: boolean) => SystemStyleObject toggleButton: SystemStyleObject actionButton: SystemStyleObject input: SystemStyleObject @@ -18,13 +18,17 @@ export const footerStyles: { aiIndicator: AIIndicatorStyles } = { footer: { - container: (isCollapsed) => ({ + container: (isCollapsed, hasAttachments) => ({ bg: isCollapsed ? 'transparent' : 'gray.800', borderTopRadius: isCollapsed ? 'none' : 'lg', transform: isCollapsed ? 'translateY(calc(100% - 24px))' : 'translateY(0)', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', - height: '100%', + height: 'auto', + minHeight: isCollapsed ? 'auto' : { base: '100px', md: '120px' }, position: 'relative', + bottom: !isCollapsed && hasAttachments + ? 'clamp(1vh, calc(110px - 5vh), 1000vh)' + : '0px', overflow: isCollapsed ? 'visible' : 'hidden', pb: '4', }), diff --git a/src/renderer/src/components/footer/footer.tsx b/src/renderer/src/components/footer/footer.tsx index 7c058614..0293ad69 100644 --- a/src/renderer/src/components/footer/footer.tsx +++ b/src/renderer/src/components/footer/footer.tsx @@ -1,18 +1,33 @@ /* eslint-disable react/require-default-props */ import { - Box, Textarea, IconButton, HStack, + Box, + Textarea, + IconButton, + HStack, + Image, } from '@chakra-ui/react'; -import { BsMicFill, BsMicMuteFill, BsPaperclip } from 'react-icons/bs'; +import { BsMicFill, BsMicMuteFill, BsPaperclip, BsX } from 'react-icons/bs'; import { IoHandRightSharp } from 'react-icons/io5'; import { FiChevronDown } from 'react-icons/fi'; -import { memo } from 'react'; +import { memo, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { InputGroup } from '@/components/ui/input-group'; +import { + DialogRoot, + DialogContent, + DialogCloseTrigger, + DialogBody, +} from '@/components/ui/dialog'; import { footerStyles } from './footer-styles'; import AIStateIndicator from './ai-state-indicator'; import { useFooter } from '@/hooks/footer/use-footer'; -// Type definitions +const MIN_THUMBNAIL_SIZE = 32; +const MAX_THUMBNAIL_SIZE = 72; +const THUMBNAIL_GAP = 8; +const REMOVE_BUTTON_RATIO = 0.34; +const MAX_ATTACHMENTS = 20; + interface FooterProps { isCollapsed?: boolean onToggle?: () => void @@ -35,9 +50,10 @@ interface MessageInputProps { onKeyDown: (e: React.KeyboardEvent) => void onCompositionStart: () => void onCompositionEnd: () => void + onAttachFiles: (files: FileList | null) => void + attachedCount: number } -// Reusable components const ToggleButton = memo(({ isCollapsed, onToggle }: ToggleButtonProps) => ( { const { t } = useTranslation(); + const fileInputRef = useRef(null); return ( - - - - - -