From 99acc22b780512d50d4a4490d19cf0e93db23e8e Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 24 Sep 2025 09:45:15 -0700 Subject: [PATCH] minimize icon for the notepad "close", fix notepad reisizing over iframes --- package.json | 2 +- src/components/App/UpdateAvailableModal.tsx | 2 +- src/components/Notepad/Container.tsx | 26 ++- src/components/Notepad/Tab.tsx | 186 +++++++++++++++++++ src/components/Notepad/Tabs.tsx | 193 +------------------- 5 files changed, 217 insertions(+), 192 deletions(-) create mode 100644 src/components/Notepad/Tab.tsx diff --git a/package.json b/package.json index 6ad549a..1d30319 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "gerbil", "productName": "Gerbil", - "version": "1.5.1", + "version": "1.5.2", "description": "Run Large Language Models locally", "main": "out/main/index.js", "homepage": "./", diff --git a/src/components/App/UpdateAvailableModal.tsx b/src/components/App/UpdateAvailableModal.tsx index ff0a1fa..ce12d07 100644 --- a/src/components/App/UpdateAvailableModal.tsx +++ b/src/components/App/UpdateAvailableModal.tsx @@ -60,7 +60,7 @@ export const UpdateAvailableModal = ({ opened={opened} onClose={onClose} size="sm" - title="A newer version is available" + title="An update is available" closeOnEscape={!isDownloading && !isUpdating} > diff --git a/src/components/Notepad/Container.tsx b/src/components/Notepad/Container.tsx index c070881..cfecb5c 100644 --- a/src/components/Notepad/Container.tsx +++ b/src/components/Notepad/Container.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState, type MouseEvent } from 'react'; import { Box, Paper, ActionIcon } from '@mantine/core'; -import { X } from 'lucide-react'; +import { Minus } from 'lucide-react'; import { useNotepadStore } from '@/stores/notepad'; import { usePreferencesStore } from '@/stores/preferences'; import { NOTEPAD_MIN_WIDTH, NOTEPAD_MIN_HEIGHT } from '@/constants/notepad'; @@ -168,6 +168,27 @@ export const NotepadContainer = () => { onMouseDown={handleResizeStart('top-right')} /> + {resizeDirection && ( + + )} + { variant="subtle" size="xs" onClick={() => setVisible(false)} - color="red" > - + diff --git a/src/components/Notepad/Tab.tsx b/src/components/Notepad/Tab.tsx new file mode 100644 index 0000000..d9cc727 --- /dev/null +++ b/src/components/Notepad/Tab.tsx @@ -0,0 +1,186 @@ +import { + type MouseEvent, + type DragEvent, + type KeyboardEvent, + useState, + useRef, + useEffect, +} from 'react'; +import { Box, ActionIcon, Text, TextInput } from '@mantine/core'; +import { X } from 'lucide-react'; +import { usePreferencesStore } from '@/stores/preferences'; + +interface TabProps { + title: string; + index: number; + isActive: boolean; + onSelect: () => void; + onClose: (e: MouseEvent) => void; + onDragStart: (e: DragEvent, index: number) => void; + onDragOver: (e: DragEvent) => void; + onDrop: (e: DragEvent, index: number) => void; + onRename: (newTitle: string) => void; + isDragOver: boolean; + showLineNumbers: boolean; + setShowLineNumbers: (show: boolean) => void; +} + +export const Tab = ({ + index, + isActive, + title, + onSelect, + onClose, + onDragStart, + onDragOver, + onDrop, + onRename, + isDragOver, + showLineNumbers, + setShowLineNumbers, +}: TabProps) => { + const { resolvedColorScheme } = usePreferencesStore(); + const [isEditing, setIsEditing] = useState(false); + const [editingTitle, setEditingTitle] = useState(title); + const inputRef = useRef(null); + + useEffect(() => { + if (isEditing && inputRef.current) { + inputRef.current.focus(); + inputRef.current.select(); + } + }, [isEditing]); + + const handleTitleClick = (e: MouseEvent) => { + e.stopPropagation(); + if (!isActive) { + onSelect(); + } + }; + + const handleTitleDoubleClick = (e: MouseEvent) => { + e.stopPropagation(); + if (isActive) { + setIsEditing(true); + setEditingTitle(title); + } + }; + + const handleInputKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Enter') { + handleSaveTitle(); + } else if (e.key === 'Escape') { + setIsEditing(false); + setEditingTitle(title); + } + }; + + const handleInputBlur = () => { + handleSaveTitle(); + }; + + const handleSaveTitle = () => { + const trimmedTitle = editingTitle.trim(); + if (trimmedTitle && trimmedTitle !== title) { + onRename(trimmedTitle); + } + setIsEditing(false); + }; + + const handleTabClick = () => { + if (!isEditing) { + onSelect(); + } + }; + + const handleMouseDown = (e: MouseEvent) => { + if (e.button === 1) { + e.preventDefault(); + onClose(e); + } + }; + + return ( + onDragStart(e, index)} + onDragOver={onDragOver} + onDrop={(e) => onDrop(e, index)} + style={{ + padding: '0.375rem 0.5rem', + backgroundColor: isActive + ? resolvedColorScheme === 'dark' + ? 'var(--mantine-color-dark-4)' + : 'var(--mantine-color-gray-1)' + : isDragOver + ? resolvedColorScheme === 'dark' + ? 'var(--mantine-color-dark-5)' + : 'var(--mantine-color-gray-2)' + : 'transparent', + borderRight: `1px solid ${ + resolvedColorScheme === 'dark' + ? 'var(--mantine-color-dark-4)' + : 'var(--mantine-color-gray-3)' + }`, + cursor: isEditing ? 'default' : 'pointer', + display: 'flex', + alignItems: 'center', + gap: '0.25rem', + minWidth: 0, + maxWidth: '7.5rem', + opacity: isDragOver ? 0.5 : 1, + }} + > + {isEditing ? ( + setEditingTitle(e.target.value)} + onKeyDown={handleInputKeyDown} + onBlur={handleInputBlur} + size="xs" + variant="unstyled" + style={{ + flex: 1, + minWidth: 0, + }} + styles={{ + input: { + fontSize: 'var(--mantine-font-size-xs)', + padding: 0, + minHeight: 'auto', + height: 'auto', + lineHeight: 1, + }, + }} + /> + ) : ( + { + e.preventDefault(); + e.stopPropagation(); + setShowLineNumbers(!showLineNumbers); + }} + style={{ + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + flex: 1, + }} + > + {title} + + )} + + + + + + ); +}; diff --git a/src/components/Notepad/Tabs.tsx b/src/components/Notepad/Tabs.tsx index 3d27963..facee49 100644 --- a/src/components/Notepad/Tabs.tsx +++ b/src/components/Notepad/Tabs.tsx @@ -1,13 +1,7 @@ -import { - type MouseEvent, - type DragEvent, - type KeyboardEvent, - useState, - useRef, - useEffect, -} from 'react'; -import { Box, ActionIcon, Text, TextInput } from '@mantine/core'; -import { X, Plus } from 'lucide-react'; +import { type MouseEvent, type DragEvent, useState } from 'react'; +import { Box, ActionIcon } from '@mantine/core'; +import { Plus } from 'lucide-react'; +import { Tab } from '@/components/Notepad/Tab'; import { useNotepadStore } from '@/stores/notepad'; import { usePreferencesStore } from '@/stores/preferences'; @@ -16,181 +10,6 @@ interface NotepadTabsProps { onCloseTab: (title: string) => void; } -interface TabProps { - title: string; - index: number; - isActive: boolean; - onSelect: () => void; - onClose: (e: MouseEvent) => void; - onDragStart: (e: DragEvent, index: number) => void; - onDragOver: (e: DragEvent) => void; - onDrop: (e: DragEvent, index: number) => void; - onRename: (newTitle: string) => void; - isDragOver: boolean; - showLineNumbers: boolean; - setShowLineNumbers: (show: boolean) => void; -} - -const Tab = ({ - index, - isActive, - title, - onSelect, - onClose, - onDragStart, - onDragOver, - onDrop, - onRename, - isDragOver, - showLineNumbers, - setShowLineNumbers, -}: TabProps) => { - const { resolvedColorScheme } = usePreferencesStore(); - const [isEditing, setIsEditing] = useState(false); - const [editingTitle, setEditingTitle] = useState(title); - const inputRef = useRef(null); - - useEffect(() => { - if (isEditing && inputRef.current) { - inputRef.current.focus(); - inputRef.current.select(); - } - }, [isEditing]); - - const handleTitleClick = (e: MouseEvent) => { - e.stopPropagation(); - if (!isActive) { - onSelect(); - } - }; - - const handleTitleDoubleClick = (e: MouseEvent) => { - e.stopPropagation(); - if (isActive) { - setIsEditing(true); - setEditingTitle(title); - } - }; - - const handleInputKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Enter') { - handleSaveTitle(); - } else if (e.key === 'Escape') { - setIsEditing(false); - setEditingTitle(title); - } - }; - - const handleInputBlur = () => { - handleSaveTitle(); - }; - - const handleSaveTitle = () => { - const trimmedTitle = editingTitle.trim(); - if (trimmedTitle && trimmedTitle !== title) { - onRename(trimmedTitle); - } - setIsEditing(false); - }; - - const handleTabClick = () => { - if (!isEditing) { - onSelect(); - } - }; - - const handleMouseDown = (e: MouseEvent) => { - if (e.button === 1) { - e.preventDefault(); - onClose(e); - } - }; - - return ( - onDragStart(e, index)} - onDragOver={onDragOver} - onDrop={(e) => onDrop(e, index)} - style={{ - padding: '6px 8px', - backgroundColor: isActive - ? resolvedColorScheme === 'dark' - ? 'var(--mantine-color-dark-4)' - : 'var(--mantine-color-gray-1)' - : isDragOver - ? resolvedColorScheme === 'dark' - ? 'var(--mantine-color-dark-5)' - : 'var(--mantine-color-gray-2)' - : 'transparent', - borderRight: `1px solid ${ - resolvedColorScheme === 'dark' - ? 'var(--mantine-color-dark-4)' - : 'var(--mantine-color-gray-3)' - }`, - cursor: isEditing ? 'default' : 'pointer', - display: 'flex', - alignItems: 'center', - gap: '4px', - minWidth: 0, - maxWidth: 120, - opacity: isDragOver ? 0.5 : 1, - }} - > - {isEditing ? ( - setEditingTitle(e.target.value)} - onKeyDown={handleInputKeyDown} - onBlur={handleInputBlur} - size="xs" - variant="unstyled" - style={{ - flex: 1, - minWidth: 0, - }} - styles={{ - input: { - fontSize: 'var(--mantine-font-size-xs)', - padding: 0, - minHeight: 'auto', - height: 'auto', - lineHeight: 1, - }, - }} - /> - ) : ( - { - e.preventDefault(); - e.stopPropagation(); - setShowLineNumbers(!showLineNumbers); - }} - style={{ - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - flex: 1, - }} - > - {title} - - )} - - - - - - ); -}; - export const NotepadTabs = ({ onCreateNewTab, onCloseTab, @@ -270,7 +89,7 @@ export const NotepadTabs = ({ }`, display: 'flex', overflow: 'hidden', - minHeight: 32, + minHeight: '2rem', }} > {tabs.map((tab, index) => ( @@ -305,7 +124,7 @@ export const NotepadTabs = ({ alignSelf: 'center', }} > - + );