Skip to content
Merged
Prev Previous commit
Next Next commit
improvement(sidebar): expand sidebar by hovering and clicking the edge (
#3830)

* improvement(sidebar): expand sidebar by hovering and clicking the edge

* improvement(sidebar): add keyboard shortcuts for new workflow/task, center search modal, fix edge ARIA

* improvement(sidebar): use Tooltip.Shortcut for inline shortcut display

* fix(sidebar): change new workflow shortcut from Mod+Shift+W to Mod+Shift+P to avoid browser close-window conflict

* fix(hotkeys): fall back to event.code for international keyboard layout compatibility

* fix(sidebar): guard add-workflow shortcut with canEdit and isCreatingWorkflow checks
  • Loading branch information
waleedlatif1 authored and Theodore Li committed Mar 28, 2026
commit dc6d0e94d8b41e4d1ba5a51375c4493b2f3db3bd
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,26 @@ function parseShortcut(shortcut: string): ParsedShortcut {
}
}

/**
* Maps a KeyboardEvent.code value to the logical key name used in shortcut definitions.
* Needed for international keyboard layouts where e.key may produce unexpected characters
* (e.g. macOS Option+letter yields 'å' instead of 'a', dead keys yield 'Dead').
*/
function codeToKey(code: string): string | undefined {
if (code.startsWith('Key')) return code.slice(3).toLowerCase()
if (code.startsWith('Digit')) return code.slice(5)
return undefined
}

function matchesShortcut(e: KeyboardEvent, parsed: ParsedShortcut): boolean {
const isMac = isMacPlatform()
const expectedCtrl = parsed.ctrl || (parsed.mod ? !isMac : false)
const expectedMeta = parsed.meta || (parsed.mod ? isMac : false)
const eventKey = e.key.length === 1 ? e.key.toLowerCase() : e.key
const keyMatches = eventKey === parsed.key || codeToKey(e.code) === parsed.key

return (
eventKey === parsed.key &&
keyMatches &&
!!e.ctrlKey === !!expectedCtrl &&
!!e.metaKey === !!expectedMeta &&
!!e.shiftKey === !!parsed.shift &&
Expand Down
12 changes: 12 additions & 0 deletions apps/sim/app/workspace/[workspaceId]/utils/commands-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import type { GlobalCommand } from '@/app/workspace/[workspaceId]/providers/glob
export type CommandId =
| 'accept-diff-changes'
| 'add-agent'
| 'add-workflow'
| 'add-task'
// | 'goto-templates'
| 'goto-logs'
| 'open-search'
Expand Down Expand Up @@ -52,6 +54,16 @@ export const COMMAND_DEFINITIONS: Record<CommandId, CommandDefinition> = {
shortcut: 'Mod+Shift+A',
allowInEditable: true,
},
'add-workflow': {
id: 'add-workflow',
shortcut: 'Mod+Shift+P',
allowInEditable: false,
},
'add-task': {
id: 'add-task',
shortcut: 'Mod+Shift+K',
allowInEditable: false,
},
// 'goto-templates': {
// id: 'goto-templates',
// shortcut: 'Mod+Y',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ export function SearchModal({
'-translate-x-1/2 fixed top-[15%] z-50 w-[500px] rounded-xl border-[4px] border-black/[0.06] bg-[var(--bg)] shadow-[0_24px_80px_-16px_rgba(0,0,0,0.15)] dark:border-white/[0.06] dark:shadow-[0_24px_80px_-16px_rgba(0,0,0,0.4)]',
open ? 'visible opacity-100' : 'invisible opacity-0'
)}
style={{ left: '50%' }}
style={{ left: 'calc(var(--sidebar-width) / 2 + 50%)' }}
>
<Command label='Search' shouldFilter={false}>
<div className='mx-2 mt-2 mb-1 flex items-center gap-1.5 rounded-lg border border-[var(--border-1)] bg-[var(--surface-5)] px-2 dark:bg-[var(--surface-4)]'>
Expand Down
Loading