Skip to content

Commit

Permalink
feat!: add keyboard shortcuts for terminal navigation (#12)
Browse files Browse the repository at this point in the history
feat!: add keyboard shortcuts for terminal navigation

<Ctrl+J> now focuses on the terminal while <Alt+J> returns to the last 
opened pane before the terminal.

This change adds two new keyboard shortcuts to improve terminal navigation:
- <Ctrl+J>: Focuses the terminal
- <Alt+J>: Returns to the previously active pane

BREAKING CHANGE: These keyboard shortcuts may conflict with existing 
user configurations and change the expected terminal navigation behavior.
  • Loading branch information
nathabonfim59 authored Dec 20, 2024
1 parent 778c670 commit c905a26
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 38 deletions.
76 changes: 76 additions & 0 deletions frontend/src/lib/components/Tooltip.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<script lang="ts">
import { onMount } from 'svelte';
import { fade } from 'svelte/transition';
export let text: string;
export let position: 'top' | 'bottom' | 'left' | 'right' = 'top';
export let delay = 300;
let tooltipVisible = false;
let tooltipElement: HTMLDivElement;
let triggerElement: HTMLDivElement;
let timeoutId: NodeJS.Timeout;
function showTooltip() {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
tooltipVisible = true;
}, delay);
}
function hideTooltip() {
clearTimeout(timeoutId);
tooltipVisible = false;
}
onMount(() => {
return () => {
clearTimeout(timeoutId);
};
});
</script>

<div
bind:this={triggerElement}
on:mouseenter={showTooltip}
on:mouseleave={hideTooltip}
on:focusin={showTooltip}
on:focusout={hideTooltip}
class="relative inline-block"
>
<slot />
{#if tooltipVisible}
<div
bind:this={tooltipElement}
transition:fade={{ duration: 100 }}
class="absolute z-50 px-2 py-1 text-xs text-white bg-gray-900 rounded shadow-lg whitespace-nowrap"
class:top-0={position === 'top'}
class:bottom-0={position === 'bottom'}
class:left-0={position === 'left'}
class:right-0={position === 'right'}
class:-translate-y-full={position === 'top'}
class:translate-y-full={position === 'bottom'}
class:-translate-x-full={position === 'left'}
class:translate-x-full={position === 'right'}
class:mb-1={position === 'top'}
class:mt-1={position === 'bottom'}
class:mr-1={position === 'left'}
class:ml-1={position === 'right'}
>
{text}
<div
class="absolute w-2 h-2 bg-gray-900 transform rotate-45"
class:left-1/2={position === 'top' || position === 'bottom'}
class:top-1/2={position === 'left' || position === 'right'}
class:-translate-x-1/2={position === 'top' || position === 'bottom'}
class:-translate-y-1/2={position === 'left' || position === 'right'}
class:bottom-0={position === 'top'}
class:top-0={position === 'bottom'}
class:right-0={position === 'left'}
class:left-0={position === 'right'}
class:translate-y-1/2={position === 'top'}
class:-translate-y-1/2={position === 'bottom'}
/>
</div>
{/if}
</div>
15 changes: 14 additions & 1 deletion frontend/src/lib/editor/Topbar.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<script lang="ts">
import { ChevronLeft, ChevronRight, Search, GitBranch, Files, File } from 'lucide-svelte';
import { ChevronLeft, ChevronRight, Search, GitBranch, Files, File, Terminal } from 'lucide-svelte';
import Input from '../components/Input.svelte';
import { tooltip } from '@/lib/actions/tooltip';
import { terminalVisibility } from '@/stores/terminalStore';
export let onToggleLeftSidebar: () => void;
export let onToggleRightSidebar: () => void;
Expand All @@ -16,6 +17,10 @@
export let modifiedFilesCount: number = 0;
let searchQuery = '';
function toggleTerminal() {
terminalVisibility.update(v => !v);
}
</script>

<div class="h-12 bg-gray-900 border-b border-gray-700 flex items-center px-4">
Expand Down Expand Up @@ -50,6 +55,14 @@
</span>
{/if}
</button>

<button
on:click={toggleTerminal}
class="p-2 hover:bg-gray-800 rounded-sm relative {$terminalVisibility ? 'bg-gray-800 text-gray-200' : 'text-gray-400'}"
use:tooltip={{ content: "Toggle Terminal", position: "bottom" }}
>
<Terminal size={16} />
</button>
</div>

<!-- Center search area with command palette and file finder -->
Expand Down
19 changes: 3 additions & 16 deletions frontend/src/lib/editor/panes/BottomPane.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import type { BottomPaneState } from '@/types/ui';
import TerminalPane from '@/lib/editor/panes/TerminalPane.svelte';
import { bottomPaneStore } from '@/stores/bottomPaneStore';
import { FilesIcon, TerminalIcon } from 'lucide-svelte';
import { FilesIcon } from 'lucide-svelte';
import { terminalVisibility } from '@/stores/terminalStore';
export let state: BottomPaneState;
export let height: number;
Expand All @@ -27,21 +28,7 @@
}
</script>

<div class="w-full flex flex-col overflow-hidden border-t border-gray-800" style="height: {height}px">
<div class="flex items-center justify-between h-[35px] px-4 border-b border-gray-800">
<div class="flex items-center space-x-2">
<span class="text-sm font-medium flex gap-2">
{#if state.activeSection === 'terminal'}
<TerminalIcon size={16} /> Terminal
{:else if state.activeSection === 'problems'}
<FilesIcon size={16} /> Problems
{:else if state.activeSection === 'output'}
<FilesIcon size={16} /> Output
{/if}
</span>
</div>
</div>

<div class="w-full flex flex-col overflow-hidden border-t border-gray-800" style="height: {!$terminalVisibility && state.activeSection === 'terminal' ? 0 : height}px">
<div class="flex-1 overflow-auto">
{#if state.activeSection === 'terminal'}
<TerminalPane {height} />
Expand Down
13 changes: 10 additions & 3 deletions frontend/src/lib/editor/panes/TerminalPane.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script lang="ts">
import XtermComponent from '@/lib/terminal/XtermComponent.svelte';
import { terminalStore, availableShells } from '@/stores/terminalStore';
import { terminalStore, availableShells, terminalVisibility } from '@/stores/terminalStore';
import { bottomPaneStore } from '@/stores/bottomPaneStore';
import { Plus, X, ChevronLeft, ChevronRight } from 'lucide-svelte';
import { Plus, X, ChevronLeft, ChevronRight, Terminal } from 'lucide-svelte';
import Button from '@/lib/components/Button.svelte';
import Select from '@/lib/components/Select.svelte';
import { get } from 'svelte/store';
Expand Down Expand Up @@ -77,7 +77,14 @@
}
</script>

<div class="h-full w-full bg-gray-800 overflow-hidden flex flex-col">
<div class="h-full w-full bg-gray-800 overflow-hidden flex flex-col" class:hidden={!$terminalVisibility}>
<div class="flex items-center justify-between h-[35px] px-4 border-b border-gray-700">
<div class="flex items-center space-x-2">
<span class="text-sm font-medium flex gap-2">
<Terminal size={16} /> Terminal
</span>
</div>
</div>
<div class="flex items-center border-b border-gray-700">
<div
bind:this={tabsContainer}
Expand Down
19 changes: 3 additions & 16 deletions frontend/src/routes/Editor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -141,30 +141,17 @@
// Save current focus
focusStore.focus('editor', $fileStore.activeFilePath || 'editor');
// Show terminal
// Just set the active section, no collapsing
bottomPaneStore.update(state => ({
...state,
collapsed: false,
activeSection: 'terminal'
}));
});
// Register return to previous shortcut
registerCommand('terminal.returnToPrevious', () => {
const previous = get(focusStore).previousContext;
if (previous && previous.component === 'editor') {
// Collapse terminal
bottomPaneStore.update(state => ({
...state,
collapsed: true
}));
// Focus editor and file
focusStore.restorePrevious();
if (previous.id !== 'editor') {
fileStore.setActiveFile(previous.id);
}
}
// Just focus back, no collapsing
focusStore.restorePrevious();
});
registerCommand('file.showFileFinder', () => showFileFinder = true);
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/stores/keyboardStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { fileStore } from './fileStore';
import { OpenConfigFile } from '@/lib/wailsjs/go/main/App';
import { push } from 'svelte-spa-router';
import { WindowReloadApp } from '@/lib/wailsjs/runtime/runtime';
import { terminalVisibility } from './terminalStore';

// Default keybindings configuration
const defaultKeybindings: KeyBindingConfig = {
Expand Down Expand Up @@ -363,7 +364,9 @@ const defaultKeybindings: KeyBindingConfig = {
category: 'Terminal',
context: ['global']
},
action: () => {}
action: () => {
terminalVisibility.set(true);
}
},
'terminal.returnToPrevious': {
defaultBinding: {
Expand All @@ -373,7 +376,9 @@ const defaultKeybindings: KeyBindingConfig = {
category: 'Terminal',
context: ['bottomPane']
},
action: () => {}
action: () => {
terminalVisibility.update(v => !v);
}
},
};

Expand Down
3 changes: 3 additions & 0 deletions frontend/src/stores/terminalStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export interface TerminalTab {
// Initialize with a default shell that will be updated
export const availableShells = writable<string[]>(['/bin/bash']);

// Initialize with visibility control
export const terminalVisibility = writable<boolean>(false);

// Load available shells on startup
GetAvailableShells().then(shells => {
availableShells.set(shells);
Expand Down

0 comments on commit c905a26

Please sign in to comment.