From 28cbbf78da6795555c3bb559a127c8da5ff690c7 Mon Sep 17 00:00:00 2001 From: Amol Sawrikar Date: Thu, 11 Sep 2025 17:30:26 +0530 Subject: [PATCH 1/3] Feat individual zoom feature --- src/App.tsx | 4 +- src/components/bottom-pane.tsx | 4 +- src/components/editor-footer.tsx | 10 +++-- src/components/editor/code-editor.tsx | 14 +++++-- src/components/layout/main-sidebar.tsx | 2 +- .../terminal/terminal-container.tsx | 15 ++++++- src/components/zoom-indicator.tsx | 2 +- src/hooks/use-active-dom-element.ts | 18 ++++++++ src/hooks/use-keyboard-shortcuts.ts | 19 +++++++-- src/hooks/use-scroll.ts | 4 +- src/stores/zoom-store.ts | 41 ++++++++++++------- 11 files changed, 100 insertions(+), 33 deletions(-) create mode 100644 src/hooks/use-active-dom-element.ts diff --git a/src/App.tsx b/src/App.tsx index fc9ca43d..cb342b62 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,6 +17,7 @@ import { import { isMac } from "./file-system/controllers/platform"; import { useRecentFoldersStore } from "./file-system/controllers/recent-folders-store"; import { useFileSystemStore } from "./file-system/controllers/store"; +import { useActiveElement } from "./hooks/use-active-dom-element"; import { useScroll } from "./hooks/use-scroll"; import { useAppStore } from "./stores/app-store"; import { useFontStore } from "./stores/font-store"; @@ -35,8 +36,7 @@ function App() { const { cleanup } = useAppStore.use.actions(); const { recentFolders, openRecentFolder } = useRecentFoldersStore(); const { loadAvailableFonts } = useFontStore.use.actions(); - const setRemoteWindow = useSidebarStore.use.setRemoteWindow(); - const zoomLevel = useZoomStore.use.zoomLevel(); + const zoomLevel = useZoomStore.use.windowZoomLevel(); // Platform-specific setup useEffect(() => { diff --git a/src/components/bottom-pane.tsx b/src/components/bottom-pane.tsx index cf8ffbb2..1b20e8e1 100644 --- a/src/components/bottom-pane.tsx +++ b/src/components/bottom-pane.tsx @@ -67,8 +67,8 @@ const BottomPane = ({ diagnostics, onDiagnosticClick }: BottomPaneProps) => { return (
{ } catch (error) { console.error("Failed to restart LSP:", error); updateToast(toastId, { - message: `Failed to restart LSP: ${error instanceof Error ? error.message : "Unknown error"}`, + message: `Failed to restart LSP: ${ + error instanceof Error ? error.message : "Unknown error" + }`, type: "error", duration: 5000, }); @@ -117,7 +119,9 @@ const LspStatusDropdown = ({ activeBuffer }: { activeBuffer: any }) => { } catch (error) { console.error("Failed to toggle LSP:", error); updateToast(toastId, { - message: `Failed to ${isCurrentlyConnected ? "disable" : "enable"} LSP: ${error instanceof Error ? error.message : "Unknown error"}`, + message: `Failed to ${isCurrentlyConnected ? "disable" : "enable"} LSP: ${ + error instanceof Error ? error.message : "Unknown error" + }`, type: "error", duration: 5000, }); @@ -237,7 +241,7 @@ const EditorFooter = () => { const cursorPosition = useEditorCursorStore.use.cursorPosition(); return ( -
+
{/* Git branch manager */} {rootFolderPath && gitStatus?.branch && ( diff --git a/src/components/editor/code-editor.tsx b/src/components/editor/code-editor.tsx index 04805f56..fba82d83 100644 --- a/src/components/editor/code-editor.tsx +++ b/src/components/editor/code-editor.tsx @@ -1,6 +1,7 @@ import type React from "react"; import { useEffect, useMemo, useRef } from "react"; import { useFileSystemStore } from "@/file-system/controllers/store"; +import { useActiveElement } from "@/hooks/use-active-dom-element"; import { useEditorScroll } from "@/hooks/use-editor-scroll"; import { useHover } from "@/hooks/use-hover"; import { LspClient } from "@/lib/lsp/lsp-client"; @@ -13,6 +14,7 @@ import { useEditorInstanceStore } from "@/stores/editor-instance-store"; import { useEditorSearchStore } from "@/stores/editor-search-store"; import { useEditorSettingsStore } from "@/stores/editor-settings-store"; import { useLspStore } from "@/stores/lsp-store"; +import { useZoomStore } from "@/stores/zoom-store"; import { useGitGutter } from "@/version-control/git/controllers/use-git-gutter"; import FindBar from "../find-bar"; import Breadcrumb from "./breadcrumb"; @@ -36,13 +38,13 @@ export interface CodeEditorRef { const CodeEditor = ({ className }: CodeEditorProps) => { const editorRef = useRef(null as any); - const { setRefs, setContent, setFileInfo } = useEditorInstanceStore(); // No longer need to sync content - editor-view-store computes from buffer const { setDisabled } = useEditorSettingsStore.use.actions(); const buffers = useBufferStore.use.buffers(); const activeBufferId = useBufferStore.use.activeBufferId(); + const zoomLevel = useZoomStore.use.editorZoomLevel(); const activeBuffer = buffers.find((b) => b.id === activeBufferId) || null; const { handleContentChange } = useAppStore.use.actions(); const { searchQuery, searchMatches, currentMatchIndex, setSearchMatches, setCurrentMatchIndex } = @@ -146,7 +148,6 @@ const CodeEditor = ({ className }: CodeEditorProps) => { // Get cursor position const cursorPosition = useEditorCursorStore.use.cursorPosition(); - // Track typing speed for dynamic debouncing const lastTypeTimeRef = useRef(Date.now()); const typingSpeedRef = useRef(500); @@ -264,7 +265,14 @@ const CodeEditor = ({ className }: CodeEditorProps) => {
{/* Hover Tooltip */} diff --git a/src/components/layout/main-sidebar.tsx b/src/components/layout/main-sidebar.tsx index 007e75ed..ba6fdeab 100644 --- a/src/components/layout/main-sidebar.tsx +++ b/src/components/layout/main-sidebar.tsx @@ -144,7 +144,7 @@ export const MainSidebar = memo(() => { }, [isGitViewActive, isSearchViewActive, isRemoteViewActive, isRemoteWindow]); return ( -
+
{/* Pane Selection Row */} (null); const [newTerminalName, setNewTerminalName] = useState(""); const hasInitializedRef = useRef(false); @@ -394,7 +398,16 @@ const TerminalContainer = ({ /> {/* Terminal Sessions */} -
+
{(() => { return (
diff --git a/src/components/zoom-indicator.tsx b/src/components/zoom-indicator.tsx index f9eb9439..e9e88ec0 100644 --- a/src/components/zoom-indicator.tsx +++ b/src/components/zoom-indicator.tsx @@ -29,7 +29,7 @@ export function ZoomIndicator() { animationFillMode: "forwards", }} > - {zoomPercentage} + Terminal: {getZoomPercentage("terminal")}% Window: {getZoomPercentage("window")}%
); } diff --git a/src/hooks/use-active-dom-element.ts b/src/hooks/use-active-dom-element.ts new file mode 100644 index 00000000..86b156f1 --- /dev/null +++ b/src/hooks/use-active-dom-element.ts @@ -0,0 +1,18 @@ +import { useEffect, useState } from "react"; + +export const useActiveElement = () => { + const [active, setActive] = useState(document.activeElement); + + const handleFocusIn = (e: FocusEvent) => { + setActive(document.activeElement); + }; + + useEffect(() => { + document.addEventListener("focusin", handleFocusIn); + return () => { + document.removeEventListener("focusin", handleFocusIn); + }; + }, []); + + return active; +}; diff --git a/src/hooks/use-keyboard-shortcuts.ts b/src/hooks/use-keyboard-shortcuts.ts index c440fa26..f941d120 100644 --- a/src/hooks/use-keyboard-shortcuts.ts +++ b/src/hooks/use-keyboard-shortcuts.ts @@ -5,6 +5,7 @@ import { isMac } from "../file-system/controllers/platform"; import type { CoreFeaturesState } from "../settings/models/feature.types"; import { useZoomStore } from "../stores/zoom-store"; import type { Buffer } from "../types/buffer"; +import { useActiveElement } from "./use-active-dom-element"; interface UseKeyboardShortcutsProps { setIsBottomPaneVisible: (value: boolean | ((prev: boolean) => boolean)) => void; @@ -62,6 +63,8 @@ export const useKeyboardShortcuts = ({ const { settings } = useSettingsStore(); const { zoomIn, zoomOut, resetZoom } = useZoomStore.use.actions(); + const activeElement = useActiveElement(); + useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Don't process regular shortcuts if vim mode is enabled and we're not using modifier keys @@ -362,19 +365,29 @@ export const useKeyboardShortcuts = ({ // Zoom controls if ((e.metaKey || e.ctrlKey) && (e.key === "=" || e.key === "+")) { e.preventDefault(); - zoomIn(); + const isEditorFocused = activeElement?.className.includes("editor"); + if (isEditorFocused) { + zoomIn("window"); + } else { + zoomIn("terminal"); + } return; } if ((e.metaKey || e.ctrlKey) && e.key === "-") { e.preventDefault(); - zoomOut(); + const isEditorFocused = activeElement?.className.includes("editor"); + if (isEditorFocused) { + zoomOut("window"); + } else { + zoomOut("terminal"); + } return; } if ((e.metaKey || e.ctrlKey) && e.key === "0") { e.preventDefault(); - resetZoom(); + resetZoom("window"); return; } diff --git a/src/hooks/use-scroll.ts b/src/hooks/use-scroll.ts index 93bd60f1..18dce86c 100644 --- a/src/hooks/use-scroll.ts +++ b/src/hooks/use-scroll.ts @@ -16,10 +16,10 @@ export function useScroll() { if (e.deltaY < 0) { // Scroll up = zoom in - zoomIn(); + zoomIn("editor"); } else if (e.deltaY > 0) { // Scroll down = zoom out - zoomOut(); + zoomOut("editor"); } } }; diff --git a/src/stores/zoom-store.ts b/src/stores/zoom-store.ts index 17e4e8fe..e1bfa950 100644 --- a/src/stores/zoom-store.ts +++ b/src/stores/zoom-store.ts @@ -4,18 +4,24 @@ import { createSelectors } from "@/utils/zustand-selectors"; const ZOOM_LEVELS = [0.5, 0.75, 0.9, 1.0, 1.1, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0]; const DEFAULT_ZOOM = 1.0; +type ZoomType = "window" | "editor" | "terminal"; + interface ZoomState { - zoomLevel: number; + // zoomLevel: number; + windowZoomLevel: number; + editorZoomLevel: number; + terminalZoomLevel: number; showZoomIndicator: boolean; zoomIndicatorTimeout: NodeJS.Timeout | null; actions: ZoomActions; } interface ZoomActions { - zoomIn: () => void; - zoomOut: () => void; - resetZoom: () => void; + zoomIn: (type: ZoomType) => void; + zoomOut: (type: ZoomType) => void; + resetZoom: (type: ZoomType) => void; showZoomIndicatorTemporarily: () => void; + getZoomPercentage: (type: ZoomType) => number; } export const useZoomStore = createSelectors( @@ -36,42 +42,47 @@ export const useZoomStore = createSelectors( }; return { - zoomLevel: DEFAULT_ZOOM, + windowZoomLevel: DEFAULT_ZOOM, + editorZoomLevel: DEFAULT_ZOOM, + terminalZoomLevel: DEFAULT_ZOOM, showZoomIndicator: false, zoomIndicatorTimeout: null, actions: { - zoomIn: () => { - const current = get().zoomLevel; + zoomIn: (type: ZoomType) => { + const current = get()[`${type}ZoomLevel`]; const currentIndex = ZOOM_LEVELS.findIndex((level) => level >= current); const nextIndex = Math.min(currentIndex + 1, ZOOM_LEVELS.length - 1); const newZoom = ZOOM_LEVELS[nextIndex]; - + console.log("zoomIn", type, newZoom); if (newZoom !== current) { - set({ zoomLevel: newZoom }); + set({ [`${type}ZoomLevel`]: newZoom }); showZoomIndicatorTemporarily(); } }, - zoomOut: () => { - const current = get().zoomLevel; + zoomOut: (type: ZoomType) => { + const current = get()[`${type}ZoomLevel`]; const currentIndex = ZOOM_LEVELS.findIndex((level) => level >= current); const prevIndex = Math.max(currentIndex - 1, 0); const newZoom = ZOOM_LEVELS[prevIndex]; if (newZoom !== current) { - set({ zoomLevel: newZoom }); + set({ [`${type}ZoomLevel`]: newZoom }); showZoomIndicatorTemporarily(); } }, - resetZoom: () => { - if (get().zoomLevel !== DEFAULT_ZOOM) { - set({ zoomLevel: DEFAULT_ZOOM }); + resetZoom: (type: ZoomType) => { + if ((get()[`${type}ZoomLevel` as keyof ZoomState] as number) !== DEFAULT_ZOOM) { + set({ [`${type}ZoomLevel`]: DEFAULT_ZOOM }); showZoomIndicatorTemporarily(); } }, showZoomIndicatorTemporarily, + + getZoomPercentage: (type: ZoomType) => + Math.round((get()[`${type}ZoomLevel` as keyof ZoomState] as number) * 100), }, }; }), From ec527286649c133dae078d989215d3718edebed5 Mon Sep 17 00:00:00 2001 From: Amol Sawrikar Date: Thu, 11 Sep 2025 18:09:25 +0530 Subject: [PATCH 2/3] Fix type errors for zoom --- src/App.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App.tsx b/src/App.tsx index cb342b62..ad3932e7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -36,6 +36,7 @@ function App() { const { cleanup } = useAppStore.use.actions(); const { recentFolders, openRecentFolder } = useRecentFoldersStore(); const { loadAvailableFonts } = useFontStore.use.actions(); + const setRemoteWindow = useSidebarStore.use.setRemoteWindow(); const zoomLevel = useZoomStore.use.windowZoomLevel(); // Platform-specific setup From 18a09c601aff2620ce3e29af66c091ed447b6b12 Mon Sep 17 00:00:00 2001 From: Amol Sawrikar Date: Thu, 11 Sep 2025 19:30:15 +0530 Subject: [PATCH 3/3] Add individual zoom to window,pane and terminal --- src/App.tsx | 1 - src/components/editor-footer.tsx | 2 +- src/components/editor/code-editor.tsx | 1 - src/components/layout/main-sidebar.tsx | 2 +- .../terminal/terminal-container.tsx | 1 - src/components/zoom-indicator.tsx | 16 +++++++--- src/hooks/use-active-dom-element.ts | 2 +- src/hooks/use-keyboard-shortcuts.ts | 31 ++++++++++++------- 8 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index ad3932e7..fc8ef995 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,7 +17,6 @@ import { import { isMac } from "./file-system/controllers/platform"; import { useRecentFoldersStore } from "./file-system/controllers/recent-folders-store"; import { useFileSystemStore } from "./file-system/controllers/store"; -import { useActiveElement } from "./hooks/use-active-dom-element"; import { useScroll } from "./hooks/use-scroll"; import { useAppStore } from "./stores/app-store"; import { useFontStore } from "./stores/font-store"; diff --git a/src/components/editor-footer.tsx b/src/components/editor-footer.tsx index 5b07c5aa..3d0813ea 100644 --- a/src/components/editor-footer.tsx +++ b/src/components/editor-footer.tsx @@ -241,7 +241,7 @@ const EditorFooter = () => { const cursorPosition = useEditorCursorStore.use.cursorPosition(); return ( -
+
{/* Git branch manager */} {rootFolderPath && gitStatus?.branch && ( diff --git a/src/components/editor/code-editor.tsx b/src/components/editor/code-editor.tsx index fba82d83..dac9b5a5 100644 --- a/src/components/editor/code-editor.tsx +++ b/src/components/editor/code-editor.tsx @@ -1,7 +1,6 @@ import type React from "react"; import { useEffect, useMemo, useRef } from "react"; import { useFileSystemStore } from "@/file-system/controllers/store"; -import { useActiveElement } from "@/hooks/use-active-dom-element"; import { useEditorScroll } from "@/hooks/use-editor-scroll"; import { useHover } from "@/hooks/use-hover"; import { LspClient } from "@/lib/lsp/lsp-client"; diff --git a/src/components/layout/main-sidebar.tsx b/src/components/layout/main-sidebar.tsx index ba6fdeab..c07939fe 100644 --- a/src/components/layout/main-sidebar.tsx +++ b/src/components/layout/main-sidebar.tsx @@ -144,7 +144,7 @@ export const MainSidebar = memo(() => { }, [isGitViewActive, isSearchViewActive, isRemoteViewActive, isRemoteWindow]); return ( -
+
{/* Pane Selection Row */} { + function getZoomPercentage(zoomLevel: number) { return `${Math.round(zoomLevel * 100)}%`; - }, [zoomLevel]); + } + + // const zoomPercentage = useMemo(() => { + // return `${Math.round(zoomLevel * 100)}%`; + // }, [zoomLevel]); if (!showZoomIndicator) { return null; @@ -29,7 +34,8 @@ export function ZoomIndicator() { animationFillMode: "forwards", }} > - Terminal: {getZoomPercentage("terminal")}% Window: {getZoomPercentage("window")}% + Window: {getZoomPercentage(windowZoomLevel)}% Terminal: {getZoomPercentage(terminalZoomLevel)} + %
); } diff --git a/src/hooks/use-active-dom-element.ts b/src/hooks/use-active-dom-element.ts index 86b156f1..93eade8b 100644 --- a/src/hooks/use-active-dom-element.ts +++ b/src/hooks/use-active-dom-element.ts @@ -3,7 +3,7 @@ import { useEffect, useState } from "react"; export const useActiveElement = () => { const [active, setActive] = useState(document.activeElement); - const handleFocusIn = (e: FocusEvent) => { + const handleFocusIn = (_e: FocusEvent) => { setActive(document.activeElement); }; diff --git a/src/hooks/use-keyboard-shortcuts.ts b/src/hooks/use-keyboard-shortcuts.ts index f941d120..ae48e78c 100644 --- a/src/hooks/use-keyboard-shortcuts.ts +++ b/src/hooks/use-keyboard-shortcuts.ts @@ -5,7 +5,8 @@ import { isMac } from "../file-system/controllers/platform"; import type { CoreFeaturesState } from "../settings/models/feature.types"; import { useZoomStore } from "../stores/zoom-store"; import type { Buffer } from "../types/buffer"; -import { useActiveElement } from "./use-active-dom-element"; + +//import { useActiveElement } from "./use-active-dom-element"; interface UseKeyboardShortcutsProps { setIsBottomPaneVisible: (value: boolean | ((prev: boolean) => boolean)) => void; @@ -63,7 +64,7 @@ export const useKeyboardShortcuts = ({ const { settings } = useSettingsStore(); const { zoomIn, zoomOut, resetZoom } = useZoomStore.use.actions(); - const activeElement = useActiveElement(); + // const activeElement = useActiveElement(); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { @@ -365,29 +366,37 @@ export const useKeyboardShortcuts = ({ // Zoom controls if ((e.metaKey || e.ctrlKey) && (e.key === "=" || e.key === "+")) { e.preventDefault(); - const isEditorFocused = activeElement?.className.includes("editor"); - if (isEditorFocused) { - zoomIn("window"); - } else { + const terminalContainer = document.querySelector('[data-terminal-container="active"]'); + const isTerminalFocused = terminalContainer?.contains(document.activeElement); + if (isTerminalFocused) { zoomIn("terminal"); + } else { + zoomIn("window"); } return; } if ((e.metaKey || e.ctrlKey) && e.key === "-") { e.preventDefault(); - const isEditorFocused = activeElement?.className.includes("editor"); - if (isEditorFocused) { - zoomOut("window"); - } else { + const terminalContainer = document.querySelector('[data-terminal-container="active"]'); + const isTerminalFocused = terminalContainer?.contains(document.activeElement); + if (isTerminalFocused) { zoomOut("terminal"); + } else { + zoomOut("window"); } return; } if ((e.metaKey || e.ctrlKey) && e.key === "0") { e.preventDefault(); - resetZoom("window"); + const terminalContainer = document.querySelector('[data-terminal-container="active"]'); + const isTerminalFocused = terminalContainer?.contains(document.activeElement); + if (isTerminalFocused) { + resetZoom("terminal"); + } else { + resetZoom("window"); + } return; }