Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/app-core/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { requestPaneMode } from './lib/pane-mode'
import { recordRendererPerf } from './lib/perf'
import { focusEditorNormalMode } from './lib/editor-focus'
import { installMarkdownFileDropHandler } from './lib/markdown-file-drop'
import { shouldBlockGlobalPaletteNavShortcut } from './lib/palette-nav-shortcuts'
import {
appUpdateNoticeLabel,
appUpdatePrimaryActionLabel,
Expand Down Expand Up @@ -475,6 +476,11 @@ function App(): JSX.Element {
}
}
}
if (shouldBlockGlobalPaletteNavShortcut(e)) {
e.preventDefault()
e.stopPropagation()
return
}

if (matchesShortcut(e, overrides, 'global.commandPalette')) {
// ⇧⌘P — command palette
Expand Down
5 changes: 3 additions & 2 deletions packages/app-core/src/components/BufferPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import { useEffect, useMemo, useRef, useState } from 'react'
import { useStore } from '../store'
import { rankItems } from '../lib/fuzzy-score'
import { isPaletteNextKey, isPalettePreviousKey } from '../lib/palette-nav'
import { isPaletteNextKey, isPalettePreviousKey, paletteNavHintLabel } from '../lib/palette-nav'
import {
allLeaves,
findLeafWithActiveTab,
Expand Down Expand Up @@ -200,6 +200,7 @@ export function BufferPalette(): JSX.Element {
const setActivePane = useStore((s) => s.setActivePane)
const focusTabInPane = useStore((s) => s.focusTabInPane)

const paletteNavKeys = useStore((s) => s.paletteNavKeys)
// Select primitives separately so each selector returns a stable
// reference; compute the derived entries list with useMemo. Returning
// a freshly-built array from a single selector would trip zustand's
Expand Down Expand Up @@ -343,7 +344,7 @@ export function BufferPalette(): JSX.Element {
<div className="flex items-center justify-end gap-4 border-t border-paper-300/70 bg-paper-100 px-4 py-2 text-xs text-ink-500">
<span>
<kbd className="rounded bg-paper-200 px-1">↑↓</kbd>{' '}
<kbd className="rounded bg-paper-200 px-1">Ctrl+N/P</kbd> move
<kbd className="rounded bg-paper-200 px-1">{paletteNavHintLabel(paletteNavKeys)}</kbd> move
</span>
<span>
<kbd className="rounded bg-paper-200 px-1">↵</kbd> switch
Expand Down
5 changes: 3 additions & 2 deletions packages/app-core/src/components/CommandPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
RECENT_COMMAND_COUNT
} from '../lib/command-history'
import { rankItems } from '../lib/fuzzy-score'
import { isPaletteNextKey, isPalettePreviousKey } from '../lib/palette-nav'
import { isPaletteNextKey, isPalettePreviousKey, paletteNavHintLabel } from '../lib/palette-nav'
import { THEMES, type ThemeFamily, type ThemeMode, type ThemeOption } from '../lib/themes'
import {
buildVaultSwitcherEntries,
Expand All @@ -36,6 +36,7 @@ export function CommandPalette(): JSX.Element {
const remoteWorkspaceProfiles = useStore((s) => s.remoteWorkspaceProfiles)
const currentVault = useStore((s) => s.vault)
const workspaceMode = useStore((s) => s.workspaceMode)
const paletteNavKeys = useStore((s) => s.paletteNavKeys)
const remoteWorkspaceInfo = useStore((s) => s.remoteWorkspaceInfo)
const initialMode = useStore((s) => s.commandPaletteInitialMode)
const refreshLocalVaults = useStore((s) => s.refreshLocalVaults)
Expand Down Expand Up @@ -438,7 +439,7 @@ export function CommandPalette(): JSX.Element {
<div className="flex items-center justify-end gap-4 border-t border-paper-300/70 bg-paper-100 px-4 py-2 text-xs text-ink-500">
<span>
<kbd className="rounded bg-paper-200 px-1">↑↓</kbd>{' '}
<kbd className="rounded bg-paper-200 px-1">Ctrl+N/P</kbd> move
<kbd className="rounded bg-paper-200 px-1">{paletteNavHintLabel(paletteNavKeys)}</kbd> move
</span>
<span>
<kbd className="rounded bg-paper-200 px-1">↵</kbd>{' '}
Expand Down
19 changes: 19 additions & 0 deletions packages/app-core/src/components/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ const DEFAULT_VIM_MAPPINGS_TO_CLEAR = [
'zR'
]

/**
* Mappings that apply in ALL contexts (no `context` property set) and
* conflict with native keybindings. `Vim.unmap(key, 'insert')` can't
* remove them because the vim unmap check (`entry.context === ctx`)
* fails when both are undefined. Pass no ctx so `undefined === undefined`
* matches the context-less entry.
*/
const VIM_MAPPINGS_TO_CLEAR_ALL_CONTEXTS = [
'<C-n>',
'<C-p>'
]

function clearKnownVimMappings(): void {
for (const binding of DEFAULT_VIM_MAPPINGS_TO_CLEAR) {
try {
Expand All @@ -69,6 +81,13 @@ function clearKnownVimMappings(): void {
/* ignore */
}
}
for (const binding of VIM_MAPPINGS_TO_CLEAR_ALL_CONTEXTS) {
try {
Vim.unmap(binding, undefined as unknown as string)
} catch {
/* ignore */
}
}
}

function toVimKeyName(base: string): string {
Expand Down
31 changes: 25 additions & 6 deletions packages/app-core/src/components/EditorPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ import { syntaxHighlighting, HighlightStyle, defaultHighlightStyle } from '@code
import { headingFolding } from '../lib/cm-heading-fold'
import { tags as t } from '@lezer/highlight'
import { searchKeymap } from '@codemirror/search'
import { autocompletion, completionKeymap } from '@codemirror/autocomplete'
import { autocompletion, completionKeymap, completionStatus, moveCompletionSelection } from '@codemirror/autocomplete'
import { useStore } from '../store'
import type { LineNumberMode } from '../store'
import type { PaneEdge, PaneLeaf } from '../lib/pane-layout'
Expand Down Expand Up @@ -110,6 +110,7 @@ import {
} from '../lib/editor-hydration'
import { recordRendererPerf } from '../lib/perf'
import { rememberTabScroll, recallTabScroll } from '../lib/tab-scroll-memory'
import { isPaletteNavShortcutKey, isPaletteNextKey, isPalettePreviousKey, paletteCompletionKeymaps, paletteNavModeClass } from '../lib/palette-nav'
import { parseOutline } from '../lib/outline'
import {
findRenderedHeadingForOutlineLine,
Expand Down Expand Up @@ -1298,10 +1299,10 @@ export function EditorPane({ pane }: { pane: PaneLeaf }): JSX.Element {
override: [slashCommandSource, dateShortcutSource, wikilinkSource],
addToOptions: [{ render: slashCommandRender.render, position: 0 }],
icons: false,
optionClass: (completion) =>
(completion as { _kind?: string })._kind === 'wikilink'
? 'wikilink-cmd-option'
: 'slash-cmd-option'
optionClass: (completion) => {
if ((completion as { _kind?: string })._kind !== 'wikilink') return 'slash-cmd-option'
return `wikilink-cmd-option ${paletteNavModeClass(useStore.getState().paletteNavKeys)}`
}
}),
keymap.of([
{
Expand All @@ -1317,7 +1318,8 @@ export function EditorPane({ pane }: { pane: PaneLeaf }): JSX.Element {
...defaultKeymap,
...historyKeymap,
...searchKeymap,
...completionKeymap
...completionKeymap,
...paletteCompletionKeymaps()
]),
EditorView.domEventHandlers({
mousedown: (event) => {
Expand Down Expand Up @@ -1378,6 +1380,23 @@ export function EditorPane({ pane }: { pane: PaneLeaf }): JSX.Element {
return true
}
}
if (completionStatus(view.state) === 'active') {
if (isPaletteNextKey(event)) {
moveCompletionSelection(true)(view)
event.preventDefault()
return true
}
if (isPalettePreviousKey(event)) {
moveCompletionSelection(false)(view)
event.preventDefault()
return true
}
if (isPaletteNavShortcutKey(event)) {
event.preventDefault()
event.stopPropagation()
return true
}
}
if (event.key !== 'Escape') return false
if (!state.vimMode) return false
const cm = getCM(view)
Expand Down
5 changes: 3 additions & 2 deletions packages/app-core/src/components/OutlinePalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { useEffect, useMemo, useRef, useState } from 'react'
import { useStore } from '../store'
import { rankItems } from '../lib/fuzzy-score'
import { isPaletteNextKey, isPalettePreviousKey } from '../lib/palette-nav'
import { isPaletteNextKey, isPalettePreviousKey, paletteNavHintLabel } from '../lib/palette-nav'
import { parseOutline, type OutlineItem } from '../lib/outline'
import { isHelpTabPath } from '@shared/help'
import { isArchiveTabPath } from '@shared/archive'
Expand All @@ -36,6 +36,7 @@ export function OutlinePalette(): JSX.Element {
const setOpen = useStore((s) => s.setOutlinePaletteOpen)
const selectedPath = useStore((s) => s.selectedPath)
const noteContents = useStore((s) => s.noteContents)
const paletteNavKeys = useStore((s) => s.paletteNavKeys)

const body =
selectedPath && !isVirtualPath(selectedPath) ? noteContents[selectedPath]?.body ?? '' : ''
Expand Down Expand Up @@ -140,7 +141,7 @@ export function OutlinePalette(): JSX.Element {
<div className="flex items-center justify-end gap-4 border-t border-paper-300/70 bg-paper-100 px-4 py-2 text-xs text-ink-500">
<span>
<kbd className="rounded bg-paper-200 px-1">↑↓</kbd>{' '}
<kbd className="rounded bg-paper-200 px-1">Ctrl+N/P</kbd> move
<kbd className="rounded bg-paper-200 px-1">{paletteNavHintLabel(paletteNavKeys)}</kbd> move
</span>
<span>
<kbd className="rounded bg-paper-200 px-1">↵</kbd> jump
Expand Down
12 changes: 7 additions & 5 deletions packages/app-core/src/components/PinnedReferencePane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { livePreviewPlugin } from '../lib/cm-live-preview'
import { headingFolding } from '../lib/cm-heading-fold'
import { slashCommandSource, slashCommandRender } from '../lib/cm-slash-commands'
import { dateShortcutSource } from '../lib/cm-date-shortcuts'
import { paletteCompletionKeymaps, paletteNavModeClass } from '../lib/palette-nav'
import { wikilinkSource } from '../lib/cm-wikilinks'
import { classifyLocalAssetHref, type LocalAssetKind } from '../lib/local-assets'
import { LazyPreview as Preview } from './LazyPreview'
Expand Down Expand Up @@ -196,10 +197,10 @@ export function PinnedReferencePane(): JSX.Element | null {
override: [slashCommandSource, dateShortcutSource, wikilinkSource],
addToOptions: [{ render: slashCommandRender.render, position: 0 }],
icons: false,
optionClass: (completion) =>
(completion as { _kind?: string })._kind === 'wikilink'
? 'wikilink-cmd-option'
: 'slash-cmd-option'
optionClass: (completion) => {
if ((completion as { _kind?: string })._kind !== 'wikilink') return 'slash-cmd-option'
return `wikilink-cmd-option ${paletteNavModeClass(useStore.getState().paletteNavKeys)}`
}
}),
keymap.of([
{
Expand All @@ -215,7 +216,8 @@ export function PinnedReferencePane(): JSX.Element | null {
...defaultKeymap,
...historyKeymap,
...searchKeymap,
...completionKeymap
...completionKeymap,
...paletteCompletionKeymaps()
]),
EditorView.updateListener.of((upd) => {
if (!upd.docChanged) return
Expand Down
5 changes: 3 additions & 2 deletions packages/app-core/src/components/SearchPalette.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useMemo, useRef, useState } from 'react'
import { useStore } from '../store'
import type { NoteMeta } from '@shared/ipc'
import { isPaletteNextKey, isPalettePreviousKey } from '../lib/palette-nav'
import { isPaletteNextKey, isPalettePreviousKey, paletteNavHintLabel } from '../lib/palette-nav'
import {
buildNoteSearchIndex,
parseNoteSearchQuery,
Expand All @@ -14,6 +14,7 @@ export function SearchPalette(): JSX.Element {
const notes = useStore((s) => s.notes)
const setSearchOpen = useStore((s) => s.setSearchOpen)
const selectNote = useStore((s) => s.selectNote)
const paletteNavKeys = useStore((s) => s.paletteNavKeys)
const [query, setQuery] = useState('')
const [active, setActive] = useState(0)
const inputRef = useRef<HTMLInputElement | null>(null)
Expand Down Expand Up @@ -124,7 +125,7 @@ export function SearchPalette(): JSX.Element {
<div className="flex items-center justify-end gap-4 border-t border-paper-300/70 bg-paper-100 px-4 py-2 text-xs text-ink-500">
<span>
<kbd className="rounded bg-paper-200 px-1">↑↓</kbd>{' '}
<kbd className="rounded bg-paper-200 px-1">Ctrl+N/P</kbd> move
<kbd className="rounded bg-paper-200 px-1">{paletteNavHintLabel(paletteNavKeys)}</kbd> move
</span>
<span>
<kbd className="rounded bg-paper-200 px-1">↵</kbd> open
Expand Down
22 changes: 21 additions & 1 deletion packages/app-core/src/components/SettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
type McpServerRuntime
} from '@shared/mcp-clients'
import { useStore } from '../store'
import type { LineNumberMode, WhichKeyHintMode } from '../store'
import type { LineNumberMode, PaletteNavKeys, WhichKeyHintMode } from '../store'
import type { KeymapDefinition, KeymapId, KeymapOverrides } from '../lib/keymaps'
import {
formatKeymapBinding,
Expand Down Expand Up @@ -201,6 +201,8 @@ export function SettingsModal(): JSX.Element {
const setVimMode = useStore((s) => s.setVimMode)
const vimInsertEscape = useStore((s) => s.vimInsertEscape)
const setVimInsertEscape = useStore((s) => s.setVimInsertEscape)
const paletteNavKeys = useStore((s) => s.paletteNavKeys)
const setPaletteNavKeys = useStore((s) => s.setPaletteNavKeys)
const keymapOverrides = useStore((s) => s.keymapOverrides)
const setKeymapBinding = useStore((s) => s.setKeymapBinding)
const resetAllKeymaps = useStore((s) => s.resetAllKeymaps)
Expand Down Expand Up @@ -855,6 +857,12 @@ export function SettingsModal(): JSX.Element {
description: 'Map a key sequence like jk or jj to Escape in insert mode.',
keywords: ['vim', 'jk', 'jj', 'escape', 'insert mode', 'esc']
},
{
id: 'palette-navigation-keys',
title: 'Panel navigation keys',
description: 'Choose whether Ctrl+N/P, Ctrl+J/K, or both move through panels and editor completions.',
keywords: ['palette', 'panel', 'completion', 'ctrl n', 'ctrl p', 'ctrl j', 'ctrl k', 'navigation']
},
{
id: 'leader-key-hints',
title: 'Leader key hints',
Expand Down Expand Up @@ -972,6 +980,18 @@ export function SettingsModal(): JSX.Element {
settingId="vim-insert-escape"
onChange={(next) => setVimInsertEscape(next ?? '')}
/>
<SegmentedRow
label="Panel navigation keys"
description="Choose which Ctrl shortcuts move through panels and editor completions."
value={paletteNavKeys}
settingId="palette-navigation-keys"
options={[
{ value: 'ctrl-np', label: 'Ctrl+N/P' },
{ value: 'ctrl-jk', label: 'Ctrl+J/K' },
{ value: 'both', label: 'Both' }
]}
onChange={(next) => setPaletteNavKeys(next as PaletteNavKeys)}
/>
<ToggleRow
label="Leader key hints"
description="Show a which-key style guide after pressing the Leader key so the next available actions stay visible."
Expand Down
5 changes: 3 additions & 2 deletions packages/app-core/src/components/TemplateEditorModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import { vim } from '@replit/codemirror-vim'
import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands'
import { markdown, markdownLanguage } from '@codemirror/lang-markdown'
import { yamlFrontmatter } from '@codemirror/lang-yaml'
import { autocompletion, completionKeymap } from '@codemirror/autocomplete'
import { syntaxHighlighting, HighlightStyle, defaultHighlightStyle } from '@codemirror/language'
import { tags as t } from '@lezer/highlight'
import { autocompletion, completionKeymap } from '@codemirror/autocomplete'
import { useStore } from '../store'
import { parseFrontmatter, slugifyTemplateName } from '@shared/template-files'
import { renderTemplate } from '../lib/template-render'
import { resolveCodeLanguage } from '../lib/cm-code-languages'
import { markdownListIndentPlugin } from '../lib/cm-markdown-list-indent'
import { paletteCompletionKeymaps } from '../lib/palette-nav'
import { templateVariableSource, TEMPLATE_VARIABLES } from '../lib/cm-template-variables'
import { templateSlashCommandSource, slashCommandRender } from '../lib/cm-slash-commands'
import { Modal } from './ui/Modal'
Expand Down Expand Up @@ -134,7 +135,7 @@ export function TemplateEditorModal({
addToOptions: [{ render: slashCommandRender.render, position: 0 }],
optionClass: () => 'slash-cmd-option'
}),
keymap.of([indentWithTab, ...completionKeymap, ...defaultKeymap, ...historyKeymap]),
keymap.of([indentWithTab, ...completionKeymap, ...paletteCompletionKeymaps(), ...defaultKeymap, ...historyKeymap]),
editorTheme,
EditorView.updateListener.of((upd) => {
if (upd.docChanged) setRaw(upd.state.doc.toString())
Expand Down
5 changes: 3 additions & 2 deletions packages/app-core/src/components/TemplatePalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { useEffect, useMemo, useRef, useState } from 'react'
import { useStore } from '../store'
import { rankItems } from '../lib/fuzzy-score'
import { isPaletteNextKey, isPalettePreviousKey } from '../lib/palette-nav'
import { isPaletteNextKey, isPalettePreviousKey, paletteNavHintLabel } from '../lib/palette-nav'
import { focusEditorNormalMode } from '../lib/editor-focus'
import { BUILTIN_TEMPLATES } from '@shared/builtin-templates'
import { mergeTemplates } from '@shared/template-files'
Expand All @@ -21,6 +21,7 @@ export function TemplatePalette(): JSX.Element {
const createFromTemplate = useStore((s) => s.createFromTemplate)
const customTemplates = useStore((s) => s.customTemplates)
const mode = useStore((s) => s.templatePaletteMode)
const paletteNavKeys = useStore((s) => s.paletteNavKeys)

const templates = useMemo(
() => mergeTemplates(BUILTIN_TEMPLATES, customTemplates),
Expand Down Expand Up @@ -130,7 +131,7 @@ export function TemplatePalette(): JSX.Element {
<div className="flex items-center justify-end gap-4 border-t border-paper-300/70 bg-paper-100 px-4 py-2 text-xs text-ink-500">
<span>
<kbd className="rounded bg-paper-200 px-1">↑↓</kbd>{' '}
<kbd className="rounded bg-paper-200 px-1">Ctrl+N/P</kbd> move
<kbd className="rounded bg-paper-200 px-1">{paletteNavHintLabel(paletteNavKeys)}</kbd> move
</span>
<span>
<kbd className="rounded bg-paper-200 px-1">↵</kbd> create
Expand Down
5 changes: 3 additions & 2 deletions packages/app-core/src/components/VaultTextSearchPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
} from '@shared/ipc'
import { useStore } from '../store'
import { resolveSystemFolderLabels } from '../lib/system-folder-labels'
import { isPaletteNextKey, isPalettePreviousKey } from '../lib/palette-nav'
import { isPaletteNextKey, isPalettePreviousKey, paletteNavHintLabel } from '../lib/palette-nav'
import { recordRendererPerf } from '../lib/perf'
import { focusEditorNormalMode } from '../lib/editor-focus'
import { Modal } from './ui/Modal'
Expand Down Expand Up @@ -145,6 +145,7 @@ export function VaultTextSearchPalette(): JSX.Element {
const backend = useStore((s) => s.vaultTextSearchBackend)
const ripgrepBinaryPath = useStore((s) => s.ripgrepBinaryPath)
const fzfBinaryPath = useStore((s) => s.fzfBinaryPath)
const paletteNavKeys = useStore((s) => s.paletteNavKeys)
const [capabilities, setCapabilities] = useState<VaultTextSearchCapabilities | null>(null)
const [query, setQuery] = useState('')
const [results, setResults] = useState<VaultTextSearchMatch[]>([])
Expand Down Expand Up @@ -466,7 +467,7 @@ export function VaultTextSearchPalette(): JSX.Element {
<div className="flex items-center justify-end gap-4 border-t border-paper-300/70 bg-paper-100 px-4 py-2 text-xs text-ink-500">
<span>
<kbd className="rounded bg-paper-200 px-1">↑↓</kbd>{' '}
<kbd className="rounded bg-paper-200 px-1">Ctrl+N/P</kbd> move
<kbd className="rounded bg-paper-200 px-1">{paletteNavHintLabel(paletteNavKeys)}</kbd> move
</span>
<span>
<kbd className="rounded bg-paper-200 px-1">↵</kbd> open
Expand Down
Loading