Add "command palette" functionality

This commit is contained in:
Jonatan Heyman 2025-04-11 00:10:29 +02:00
parent 85b91f0228
commit addf310ae1
6 changed files with 96 additions and 24 deletions

View File

@ -65,6 +65,7 @@
showCreateBuffer(value) { this.dialogWatcher(value) },
showEditBuffer(value) { this.dialogWatcher(value) },
showMoveToBufferSelector(value) { this.dialogWatcher(value) },
showCommandPalette(value) { this.dialogWatcher(value) },
currentBufferPath() {
this.focusEditor()
@ -85,10 +86,11 @@
"showCreateBuffer",
"showEditBuffer",
"showMoveToBufferSelector",
"showCommandPalette",
]),
dialogVisible() {
return this.showLanguageSelector || this.showBufferSelector || this.showCreateBuffer || this.showEditBuffer || this.showMoveToBufferSelector
return this.showLanguageSelector || this.showBufferSelector || this.showCreateBuffer || this.showEditBuffer || this.showMoveToBufferSelector || this.showCommandPalette
},
editorInert() {
@ -176,7 +178,9 @@
@close="closeDialog"
/>
<BufferSelector
v-if="showBufferSelector"
v-if="showBufferSelector || showCommandPalette"
:initialFilter="showCommandPalette ? '>' : ''"
:commandsEnabled="true"
@openBuffer="openBuffer"
@openCreateBuffer="(nameSuggestion) => openCreateBuffer('new', nameSuggestion)"
@close="closeBufferSelector"
@ -184,6 +188,7 @@
<BufferSelector
v-if="showMoveToBufferSelector"
headline="Move block to..."
:commandsEnabled="false"
@openBuffer="onMoveCurrentBlockToOtherEditor"
@openCreateBuffer="(nameSuggestion) => openCreateBuffer('currentBlock', nameSuggestion)"
@close="closeMoveToBufferSelector"

View File

@ -4,6 +4,7 @@
import { mapState, mapActions } from 'pinia'
import { SCRATCH_FILE_NAME } from "../common/constants"
import { useHeynoteStore } from "../stores/heynote-store"
import { HEYNOTE_COMMANDS } from '../editor/commands'
const pathSep = window.heynote.buffer.pathSeparator
@ -19,13 +20,15 @@
export default {
props: {
headline: String,
initialFilter: String,
commandsEnabled: Boolean,
},
data() {
return {
selected: 0,
actionButton: 0,
filter: "",
filter: this.initialFilter || "",
items: [],
SCRATCH_FILE_NAME: SCRATCH_FILE_NAME,
deleteConfirm: false,
@ -48,6 +51,14 @@
"recentBufferPaths",
]),
commands() {
return Object.keys(HEYNOTE_COMMANDS).map(cmd => ({
name: cmd,
cmd: cmd,
isCommand: true,
}))
},
orderedItems() {
const sortKeys = Object.fromEntries(this.recentBufferPaths.map((item, idx) => [item, idx]))
const getSortScore = (item) => sortKeys[item.path] !== undefined ? sortKeys[item.path] : 1000
@ -74,32 +85,47 @@
},
filteredItems() {
let items
if (this.filter === "") {
items = this.orderedItems
} else {
const searchResults = fuzzysort.go(this.filter, this.items, {
keys: ["name", "folder"],
if (this.commandsEnabled && this.filter.startsWith(">")) {
// command mode if the first character is ">"
if (this.filter.length < 2) {
return this.commands
}
const searchResults = fuzzysort.go(this.filter.slice(1), this.commands, {
keys: ["name"],
})
items = searchResults.map((result) => {
return searchResults.map((result) => {
const obj = {...result.obj}
const nameHighlight = result[0].highlight("<b>", "</b>")
const folderHighlight = result[1].highlight("<b>", "</b>")
obj.name = nameHighlight !== "" ? nameHighlight : obj.name
obj.folder = folderHighlight !== "" ? folderHighlight : obj.folder
return obj
})
}
} else {
let items
if (this.filter === "") {
items = this.orderedItems
} else {
const searchResults = fuzzysort.go(this.filter, this.items, {
keys: ["name", "folder"],
})
items = searchResults.map((result) => {
const obj = {...result.obj}
const nameHighlight = result[0].highlight("<b>", "</b>")
const folderHighlight = result[1].highlight("<b>", "</b>")
obj.name = nameHighlight !== "" ? nameHighlight : obj.name
obj.folder = folderHighlight !== "" ? folderHighlight : obj.folder
return obj
})
}
const newNoteItem = {
name: "Create new…",
createNew:true,
const newNoteItem = {
name: "Create new…",
createNew:true,
}
return [
...items,
newNoteItem,
]
}
return [
...items,
newNoteItem,
]
},
},
@ -108,6 +134,7 @@
"updateBuffers",
"editBufferMetadata",
"deleteBuffer",
"executeCommand",
]),
buildItems() {
@ -187,13 +214,18 @@
} else {
this.$emit("openCreateBuffer", "")
}
} else if (item.isCommand) {
this.$emit("close")
this.$nextTick(() => {
this.executeCommand(item.cmd)
})
} else {
this.$emit("openBuffer", item.path)
}
},
itemHasActionButtons(item) {
return !item.createNew && item.path !== SCRATCH_FILE_NAME
return !item.createNew && item.path !== SCRATCH_FILE_NAME && !item.isCommand
},
onInput(event) {
@ -248,7 +280,7 @@
</script>
<template>
<form class="note-selector" tabindex="-1" @focusout="onFocusOut" ref="container">
<form class="note-selector" tabindex="-1" @focusout="onFocusOut" ref="container" @submit.prevent>
<div class="input-container">
<h1 v-if="headline">{{headline}}</h1>
<input

View File

@ -46,6 +46,10 @@ const openBufferSelector = (editor) => () => {
editor.openBufferSelector()
return true
}
const openCommandPalette = (editor) => () => {
editor.openCommandPalette()
return true
}
const openMoveToBuffer = (editor) => () => {
editor.openMoveToBufferSelector()
return true
@ -69,6 +73,7 @@ const HEYNOTE_COMMANDS = {
openLanguageSelector,
openBufferSelector,
openCommandPalette,
openMoveToBuffer,
openCreateNewBuffer,

View File

@ -22,6 +22,7 @@ import { languageDetection } from "./language-detection/autodetect.js"
import { autoSaveContent } from "./save.js"
import { todoCheckboxPlugin} from "./todo-checkbox.ts"
import { links } from "./links.js"
import { HEYNOTE_COMMANDS } from "./commands.js";
import { NoteFormat } from "../common/note-format.js"
import { AUTO_SAVE_INTERVAL } from "../common/constants.js"
import { useHeynoteStore } from "../stores/heynote-store.js";
@ -294,6 +295,10 @@ export class HeynoteEditor {
this.notesStore.openBufferSelector()
}
openCommandPalette() {
this.notesStore.openCommandPalette()
}
openCreateBuffer(createMode) {
this.notesStore.openCreateBuffer(createMode)
}
@ -428,6 +433,15 @@ export class HeynoteEditor {
selectAll() {
selectAll(this.view)
}
executeCommand(command) {
const cmd = HEYNOTE_COMMANDS[command]
if (!cmd) {
console.error(`Command not found: ${command}`)
return
}
cmd(this)(this.view)
}
}

View File

@ -104,6 +104,7 @@ export const DEFAULT_KEYMAP = [
cmd("Mod-l", "openLanguageSelector"),
cmd("Mod-p", "openBufferSelector"),
cmd("Mod-Shift-p", "openCommandPalette"),
cmd("Mod-s", "openMoveToBuffer"),
cmd("Mod-n", "openCreateNewBuffer"),

View File

@ -28,6 +28,7 @@ export const useHeynoteStore = defineStore("heynote", {
showCreateBuffer: false,
showEditBuffer: false,
showMoveToBufferSelector: false,
showCommandPalette: false,
}),
actions: {
@ -58,6 +59,11 @@ export const useHeynoteStore = defineStore("heynote", {
openBufferSelector() {
this.closeDialog()
this.showBufferSelector = true
},
openCommandPalette() {
this.closeDialog()
this.showCommandPalette = true
},
openMoveToBufferSelector() {
this.closeDialog()
@ -78,10 +84,12 @@ export const useHeynoteStore = defineStore("heynote", {
this.showLanguageSelector = false
this.showEditBuffer = false
this.showMoveToBufferSelector = false
this.showCommandPalette = false
},
closeBufferSelector() {
this.showBufferSelector = false
this.showCommandPalette = false
},
closeMoveToBufferSelector() {
@ -96,6 +104,13 @@ export const useHeynoteStore = defineStore("heynote", {
this.showEditBuffer = true
},
executeCommand(command) {
if (this.currentEditor) {
toRaw(this.currentEditor).executeCommand(command)
}
},
/**
* Create a new note file at `path` with name `name` from the current block of the current open editor,
* and switch to it