mirror of
https://github.com/purocean/yn.git
synced 2026-09-24 15:46:02 +08:00
refactor: remove ctx.command
This commit is contained in:
+1
-5
@@ -50,7 +50,6 @@ Yank Note has some concepts that are the basis for supporting the entire plug-in
|
||||
|
||||
1. Hook
|
||||
1. Action
|
||||
1. Command
|
||||
|
||||
### Hook
|
||||
|
||||
@@ -78,13 +77,10 @@ For internal hook types, please refer to [Api Document](https://yn-api-doc.verce
|
||||
|
||||
### Action
|
||||
|
||||
Yank Note has an Action Center [`ctx.action`](https://yn-api-doc.vercel.app/modules/renderer_core_action.html), which provides action management and operation。
|
||||
Yank Note has an Action Center [`ctx.action`](https://yn-api-doc.vercel.app/modules/renderer_core_action.html), which provides action and keyboard shortcuts management and operation。
|
||||
|
||||
For internal action, please refer to [Api Document](https://yn-api-doc.vercel.app/modules/renderer_types.html#BuildInActions)
|
||||
|
||||
### Command
|
||||
Yank Note has a Command Center [`ctx.command`](https://yn-api-doc.vercel.app/modules/renderer_core_command.html), which is mainly responsible for the management and operation of shortcut keys.
|
||||
|
||||
## Plug-In Capabilities
|
||||
|
||||
As you can see from the above, the functions of the plug-in are all implemented through the modules of `ctx`.
|
||||
|
||||
@@ -50,7 +50,6 @@ Yank Note 有一些概念,是支撑整个插件体系的基础:
|
||||
|
||||
1. Hook 钩子
|
||||
1. Action 动作
|
||||
1. Command 命令
|
||||
|
||||
### Hook 钩子
|
||||
|
||||
@@ -60,7 +59,7 @@ Yank Note 在执行一些操作的时候,会触发一些钩子调用。
|
||||
|
||||
调用 `triggerHook` 时候附加选项 `{ breakable: true }`,表明这个钩子调用是可中断的。
|
||||
|
||||
下面的内部钩子调用是可中断的
|
||||
如下面的内部钩子调用是可中断的
|
||||
|
||||
- `ACTION_AFTER_RUN`
|
||||
- `ACTION_BEFORE_RUN`
|
||||
@@ -76,14 +75,10 @@ Yank Note 在执行一些操作的时候,会触发一些钩子调用。
|
||||
|
||||
### Action 动作
|
||||
|
||||
Yank Note 有一个 Action 中心 [`ctx.action`](https://yn-api-doc.vercel.app/modules/renderer_core_action.html),提供动作的管理和运行。
|
||||
Yank Note 有一个 Action 中心 [`ctx.action`](https://yn-api-doc.vercel.app/modules/renderer_core_action.html),提供动作和快捷键的管理和运行。
|
||||
|
||||
内部 Action 可以参考 [Api 文档](https://yn-api-doc.vercel.app/modules/renderer_types.html#BuildInActions)
|
||||
|
||||
### Command 命令
|
||||
|
||||
Yank Note 有一个 Command 中心 [`ctx.command`](https://yn-api-doc.vercel.app/modules/renderer_core_command.html),主要负责快捷键相关的管理和运行。
|
||||
|
||||
## 插件能力
|
||||
|
||||
从上面可以看到,插件的功能都是通过 `ctx` 下面挂载的模块来实现。
|
||||
|
||||
@@ -36,13 +36,12 @@
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, ref, toRef } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { registerAction, removeAction } from '@fe/core/action'
|
||||
import { getKeysLabel, registerAction, removeAction } from '@fe/core/action'
|
||||
import { useContextMenu } from '@fe/support/ui/context-menu'
|
||||
import type { AppState } from '@fe/support/store'
|
||||
import { useI18n } from '@fe/services/i18n'
|
||||
import { toggleOutline, ControlCenter } from '@fe/services/workbench'
|
||||
import { findInRepository } from '@fe/services/base'
|
||||
import { getKeysLabel } from '@fe/core/command'
|
||||
import type { FileSort, Components } from '@fe/types'
|
||||
import SvgIcon from './SvgIcon.vue'
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, ref } from 'vue'
|
||||
import { registerAction, removeAction } from '@fe/core/action'
|
||||
import { Alt, Escape, getKeysLabel } from '@fe/core/command'
|
||||
import { getKeysLabel, Keys, registerAction, removeAction } from '@fe/core/action'
|
||||
import { ControlCenter, FileTabs } from '@fe/services/workbench'
|
||||
import { t } from '@fe/services/i18n'
|
||||
import type { Components } from '@fe/types'
|
||||
@@ -43,13 +42,13 @@ registerAction({
|
||||
registerAction({
|
||||
name: 'control-center.toggle',
|
||||
handler: toggle,
|
||||
keys: [Alt, 'c']
|
||||
keys: [Keys.Alt, 'c']
|
||||
})
|
||||
|
||||
registerAction({
|
||||
name: 'control-center.hide',
|
||||
handler: () => toggle(false),
|
||||
keys: [Escape],
|
||||
keys: [Keys.Escape],
|
||||
when: () => visible.value
|
||||
})
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ function getContentHtml () {
|
||||
return refView.value?.outerHTML || ''
|
||||
}
|
||||
|
||||
function refresh () {
|
||||
function rerender () {
|
||||
logger.debug('refresh')
|
||||
triggerHook('VIEW_BEFORE_REFRESH')
|
||||
renderDebounce()
|
||||
@@ -220,7 +220,7 @@ onMounted(() => {
|
||||
triggerHook('VIEW_MOUNTED')
|
||||
registerAction({ name: 'view.render-immediately', handler: render.bind(null, false) })
|
||||
registerAction({ name: 'view.render', handler: renderDebounce })
|
||||
registerAction({ name: 'view.refresh', handler: refresh })
|
||||
registerAction({ name: 'view.rerender', handler: rerender })
|
||||
registerAction({ name: 'view.reveal-line', handler: revealLine })
|
||||
registerAction({ name: 'view.get-content-html', handler: getContentHtml })
|
||||
registerAction({ name: 'view.get-view-dom', handler: getViewDom })
|
||||
|
||||
@@ -57,9 +57,8 @@ import dayjs from 'dayjs'
|
||||
import type * as Monaco from 'monaco-editor'
|
||||
import { useStore } from 'vuex'
|
||||
import { ref, onMounted, onUnmounted, watch, toRef, computed } from 'vue'
|
||||
import { removeAction, registerAction } from '@fe/core/action'
|
||||
import { removeAction, registerAction, Keys } from '@fe/core/action'
|
||||
import { registerHook, removeHook } from '@fe/core/hook'
|
||||
import { Alt } from '@fe/core/command'
|
||||
import { commentHistoryVersion, deleteHistoryVersion, fetchHistoryContent, fetchHistoryList } from '@fe/support/api'
|
||||
import { getDefaultOptions, getMonaco, setValue, whenEditorReady } from '@fe/services/editor'
|
||||
import { isEncrypted, isSameFile } from '@fe/services/document'
|
||||
@@ -335,7 +334,7 @@ watch(currentVersion, async val => {
|
||||
watch([content, displayType, refEditor], updateEditor)
|
||||
|
||||
onMounted(() => {
|
||||
registerAction({ name: 'doc.show-history', handler: show, keys: [Alt, 'h'] })
|
||||
registerAction({ name: 'doc.show-history', handler: show, keys: [Keys.Alt, 'h'] })
|
||||
registerAction({ name: 'doc.hide-history', handler: hide })
|
||||
|
||||
registerHook('GLOBAL_RESIZE', layoutEditor)
|
||||
|
||||
@@ -17,10 +17,9 @@
|
||||
<script lang="ts">
|
||||
import { useStore } from 'vuex'
|
||||
import { computed, defineComponent, onBeforeMount, onBeforeUnmount, ref, toRefs, watch } from 'vue'
|
||||
import { Alt, CtrlCmd, getKeysLabel, Shift } from '@fe/core/command'
|
||||
import type { Components, Doc } from '@fe/types'
|
||||
import { registerHook, removeHook } from '@fe/core/hook'
|
||||
import { registerAction, removeAction } from '@fe/core/action'
|
||||
import { getKeysLabel, Keys, registerAction, removeAction } from '@fe/core/action'
|
||||
import { ensureCurrentFileSaved, isEncrypted, isMarkdownFile, isSameFile, isSubOrSameFile, switchDoc, toUri } from '@fe/services/document'
|
||||
import type { AppState } from '@fe/support/store'
|
||||
import { useI18n } from '@fe/services/i18n'
|
||||
@@ -44,7 +43,7 @@ export default defineComponent({
|
||||
const list = ref<Components.FileTabs.Item[]>([])
|
||||
const current = ref(blankUri)
|
||||
const refTabs = ref<InstanceType<typeof Tabs> | null>(null)
|
||||
const showFilterBtnShortcuts = [Shift, Alt, 'p']
|
||||
const showFilterBtnShortcuts = [Keys.Shift, Keys.Alt, 'p']
|
||||
const filterBtnTitle = computed(() => $t.value('tabs.search-tabs') + ' ' + getKeysLabel(showFilterBtnShortcuts))
|
||||
const actionBtns = ref<Components.Tabs.ActionBtn[]>([])
|
||||
|
||||
@@ -191,7 +190,7 @@ export default defineComponent({
|
||||
|
||||
registerAction({
|
||||
name: 'file-tabs.switch-left',
|
||||
keys: [CtrlCmd, Alt, 'ArrowLeft'],
|
||||
keys: [Keys.CtrlCmd, Keys.Alt, 'ArrowLeft'],
|
||||
handler () {
|
||||
const prev = findTab(-1)
|
||||
prev && switchTab(prev)
|
||||
@@ -204,13 +203,13 @@ export default defineComponent({
|
||||
const next = findTab(1)
|
||||
next && switchTab(next)
|
||||
},
|
||||
keys: [CtrlCmd, Alt, 'ArrowRight']
|
||||
keys: [Keys.CtrlCmd, Keys.Alt, 'ArrowRight']
|
||||
})
|
||||
|
||||
registerAction({
|
||||
name: 'file-tabs.close-current',
|
||||
handler: closeCurrent,
|
||||
keys: isElectron ? [CtrlCmd, 'w'] : [CtrlCmd, Alt, 'w']
|
||||
keys: isElectron ? [Keys.CtrlCmd, 'w'] : [Keys.CtrlCmd, Keys.Alt, 'w']
|
||||
})
|
||||
|
||||
registerAction({
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onMounted, onUnmounted, ref } from 'vue'
|
||||
import type { Doc } from '@fe/types'
|
||||
import { registerAction, removeAction } from '@fe/core/action'
|
||||
import { CtrlCmd } from '@fe/core/command'
|
||||
import { Keys, registerAction, removeAction } from '@fe/core/action'
|
||||
import { switchDoc } from '@fe/services/document'
|
||||
import XMask from './Mask.vue'
|
||||
import QuickOpen from './QuickOpen.vue'
|
||||
@@ -45,7 +44,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
registerAction({ name: 'filter.show-quick-open', handler: showQuickOpen, keys: [CtrlCmd, 'p'] })
|
||||
registerAction({ name: 'filter.show-quick-open', handler: showQuickOpen, keys: [Keys.CtrlCmd, 'p'] })
|
||||
registerAction({ name: 'filter.choose-document', handler: chooseDocument })
|
||||
})
|
||||
|
||||
|
||||
@@ -124,8 +124,7 @@ import { computed, Fragment, h, nextTick, onBeforeUnmount, reactive, ref, shallo
|
||||
import type { ISearchRange, ISerializedFileMatch, ISerializedSearchSuccess, ITextQuery, ITextSearchMatch } from 'ripgrep-wrapper'
|
||||
import { getLogger, sleep } from '@fe/utils'
|
||||
import { basename, dirname, join, relative } from '@fe/utils/path'
|
||||
import { registerAction, removeAction } from '@fe/core/action'
|
||||
import { CtrlCmd, Shift } from '@fe/core/command'
|
||||
import { Keys, registerAction, removeAction } from '@fe/core/action'
|
||||
import { useLazyRef } from '@fe/utils/composable'
|
||||
import * as api from '@fe/support/api'
|
||||
import store from '@fe/support/store'
|
||||
@@ -505,7 +504,7 @@ function markText (text: string, ranges: ISearchRange[]) {
|
||||
|
||||
registerAction({
|
||||
name: 'base.find-in-repository',
|
||||
keys: [CtrlCmd, Shift, 'f'],
|
||||
keys: [Keys.CtrlCmd, Keys.Shift, 'f'],
|
||||
handler: (query?: FindInRepositoryQuery) => {
|
||||
visible.value = true
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import * as ioc from '@fe/core/ioc'
|
||||
import * as plugin from '@fe/core/plugin'
|
||||
import * as hook from '@fe/core/hook'
|
||||
import * as action from '@fe/core/action'
|
||||
import * as command from '@fe/core/command'
|
||||
import { useToast } from '@fe/support/ui/toast'
|
||||
import { useModal } from '@fe/support/ui/modal'
|
||||
import { useQuickFilter } from '@fe/support/ui/quick-filter'
|
||||
@@ -43,7 +42,6 @@ const ctx = Object.freeze({
|
||||
store,
|
||||
action,
|
||||
doc,
|
||||
command,
|
||||
tree,
|
||||
workbench,
|
||||
markdown,
|
||||
|
||||
+209
-23
@@ -1,9 +1,23 @@
|
||||
import { upperFirst } from 'lodash-es'
|
||||
import { getLogger } from '@fe/utils'
|
||||
import { BuildInActions, BuildInActionName } from '@fe/types'
|
||||
import { registerCommand, removeCommand } from './command'
|
||||
import type { BuildInActions, BuildInActionName } from '@fe/types'
|
||||
import { isMacOS } from '@fe/support/env'
|
||||
import { FLAG_DISABLE_SHORTCUTS } from '@fe/support/args'
|
||||
import { triggerHook } from './hook'
|
||||
|
||||
const logger = getLogger('action')
|
||||
export namespace Keys {
|
||||
export const Escape = 'Escape'
|
||||
export const Ctrl = 'Ctrl'
|
||||
export const Meta = 'Meta'
|
||||
export const CtrlCmd = 'CtrlCmd'
|
||||
export const Alt = 'Alt'
|
||||
export const Space = 'Space'
|
||||
export const Shift = 'Shift'
|
||||
export const BracketLeft = 'BracketLeft'
|
||||
export const BracketRight = 'BracketRight'
|
||||
export const LeftClick = 0
|
||||
export const Tab = 'Tab'
|
||||
}
|
||||
|
||||
export type ActionHandler<T extends string> = T extends BuildInActionName ? BuildInActions[T] : (...args: any[]) => any
|
||||
export type HookType = 'before-run' | 'after-run'
|
||||
@@ -30,8 +44,23 @@ export interface Action<T extends string> {
|
||||
when?: () => boolean
|
||||
}
|
||||
|
||||
const logger = getLogger('action')
|
||||
|
||||
const actions: { [id: string]: Action<string> } = {}
|
||||
|
||||
type XKey = typeof Keys.Ctrl | typeof Keys.CtrlCmd | typeof Keys.Alt | typeof Keys.Shift
|
||||
|
||||
let keys: Record<string, boolean> = {}
|
||||
|
||||
function recordKeys (e: KeyboardEvent) {
|
||||
if (e.type === 'keydown') {
|
||||
keys[e.key.toUpperCase()] = true
|
||||
} else {
|
||||
// keyup event not fired some times such as in key combination.
|
||||
keys = {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an action.
|
||||
* @param action
|
||||
@@ -39,35 +68,36 @@ const actions: { [id: string]: Action<string> } = {}
|
||||
*/
|
||||
export function registerAction<T extends string> (action: Action<T>) {
|
||||
logger.debug('registerAction', action.name)
|
||||
actions[action.name] = action
|
||||
if (action.keys) {
|
||||
registerCommand({
|
||||
id: action.name,
|
||||
keys: action.keys,
|
||||
handler: getActionHandler(action.name),
|
||||
when: action.when,
|
||||
})
|
||||
|
||||
if (actions[action.name]) {
|
||||
throw new Error(`Action ${action.name} has been registered.`)
|
||||
}
|
||||
|
||||
actions[action.name] = action
|
||||
return action
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an action handler.
|
||||
* @param name
|
||||
* @param action
|
||||
*/
|
||||
export function getActionHandler <T extends BuildInActionName> (name: T): ActionHandler<T>
|
||||
export function getActionHandler <T extends string> (name: T): ActionHandler<T>
|
||||
export function getActionHandler <T extends string> (name: T): ActionHandler<T> {
|
||||
export function getActionHandler <T extends BuildInActionName> (action: Action<T>): ActionHandler<T>
|
||||
export function getActionHandler <T extends BuildInActionName> (action: Action<string>): ActionHandler<T>
|
||||
export function getActionHandler <T extends BuildInActionName> (action: T): ActionHandler<T>
|
||||
export function getActionHandler <T extends string> (action: T): ActionHandler<T>
|
||||
export function getActionHandler <T extends string> (action: T | Action<T>): ActionHandler<T> {
|
||||
const _action = typeof action === 'string' ? getAction(action) : action
|
||||
const name = typeof action === 'string' ? action : action.name
|
||||
|
||||
logger.debug('getActionHandler', name)
|
||||
return ((...args: any[]) => {
|
||||
triggerHook('ACTION_BEFORE_RUN', { name }, { breakable: true })
|
||||
|
||||
let result: any
|
||||
|
||||
const action = getAction(name)
|
||||
if (action) {
|
||||
if (!(action.when && !action.when())) {
|
||||
result = (action.handler)?.(...args)
|
||||
if (_action) {
|
||||
if (!(_action.when && !_action.when())) {
|
||||
result = (_action.handler)?.(...args)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,10 +127,166 @@ export function removeAction (name: string) {
|
||||
logger.debug('removeAction', name)
|
||||
const action = getAction(name)
|
||||
if (action) {
|
||||
if (action.keys) {
|
||||
removeCommand(name)
|
||||
}
|
||||
|
||||
delete actions[name]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user has pressed the key
|
||||
* @param key upper case.
|
||||
*/
|
||||
export function isKeydown (key: string) {
|
||||
return !!keys[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user has pressed the Cmd key (macOS) or Ctrl key.
|
||||
* @param e
|
||||
* @returns
|
||||
*/
|
||||
export function hasCtrlCmd (e: KeyboardEvent | MouseEvent) {
|
||||
return isMacOS ? e.metaKey : e.ctrlKey
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key label.
|
||||
* @param key
|
||||
* @returns
|
||||
*/
|
||||
export function getKeyLabel (key: XKey | string | number) {
|
||||
const str = {
|
||||
CtrlCmd: isMacOS ? '⌘' : 'Ctrl',
|
||||
Alt: isMacOS ? '⌥' : 'Alt',
|
||||
Ctrl: isMacOS ? '⌃' : 'Ctrl',
|
||||
Shift: isMacOS ? '⇧' : 'Shift',
|
||||
BracketLeft: '[',
|
||||
BracketRight: ']',
|
||||
Period: '.',
|
||||
Tab: 'Tab',
|
||||
}[key]
|
||||
|
||||
return str || upperFirst(key.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the event matches the shortcut key combination.
|
||||
* @param e
|
||||
* @param keys
|
||||
* @returns
|
||||
*/
|
||||
export function matchKeys (e: KeyboardEvent | MouseEvent, keys: (string | number)[]) {
|
||||
const modifiers = { metaKey: false, ctrlKey: false, altKey: false, shiftKey: false }
|
||||
|
||||
for (const key of keys) {
|
||||
switch (key) {
|
||||
case Keys.CtrlCmd:
|
||||
if (isMacOS) {
|
||||
modifiers.metaKey = true
|
||||
} else {
|
||||
modifiers.ctrlKey = true
|
||||
}
|
||||
if (!hasCtrlCmd(e)) return false
|
||||
break
|
||||
case Keys.Alt:
|
||||
modifiers.altKey = true
|
||||
if (!e.altKey) return false
|
||||
break
|
||||
case Keys.Ctrl:
|
||||
modifiers.ctrlKey = true
|
||||
if (!e.ctrlKey) return false
|
||||
break
|
||||
case Keys.Meta:
|
||||
modifiers.metaKey = true
|
||||
if (!e.ctrlKey) return false
|
||||
break
|
||||
case Keys.Shift:
|
||||
modifiers.shiftKey = true
|
||||
if (!e.shiftKey) return false
|
||||
break
|
||||
default:
|
||||
// if the event from iframe, it not instance of KeyboardEvent.
|
||||
if (e instanceof KeyboardEvent || '' + e === '[object KeyboardEvent]') {
|
||||
e = e as KeyboardEvent
|
||||
if (
|
||||
key !== e.key &&
|
||||
key.toString().toUpperCase() !== e.code.toUpperCase() &&
|
||||
`Key${key}`.toUpperCase() !== e.code.toUpperCase()
|
||||
) return false
|
||||
} else {
|
||||
if (key !== e.button) return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modifiers.altKey === e.altKey &&
|
||||
modifiers.ctrlKey === e.ctrlKey &&
|
||||
modifiers.metaKey === e.metaKey &&
|
||||
modifiers.shiftKey === e.shiftKey
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the event shortcut key combination matches a action.
|
||||
* @param e
|
||||
* @param action
|
||||
* @returns
|
||||
*/
|
||||
export function isAction (e: KeyboardEvent | MouseEvent, action: string | Action<any>) {
|
||||
const _action = typeof action === 'string' ? getAction(action) : action
|
||||
return !!(_action && _action.keys && matchKeys(e, _action.keys))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shortcuts label.
|
||||
* @param nameOrKeys action name or keys
|
||||
* @returns
|
||||
*/
|
||||
export function getKeysLabel (name: string): string
|
||||
export function getKeysLabel (keys: (string | number)[]): string
|
||||
export function getKeysLabel (nameOrKeys: (string | number)[] | string): string {
|
||||
let keys = []
|
||||
|
||||
if (typeof nameOrKeys === 'string') {
|
||||
const action = getAction(nameOrKeys)
|
||||
if (!action || !action.keys) {
|
||||
return ''
|
||||
}
|
||||
|
||||
keys = action.keys
|
||||
} else {
|
||||
keys = nameOrKeys
|
||||
}
|
||||
|
||||
return keys.map(getKeyLabel).join(isMacOS ? ' ' : '+')
|
||||
}
|
||||
|
||||
export function keydownHandler (e: KeyboardEvent) {
|
||||
recordKeys(e)
|
||||
|
||||
if (FLAG_DISABLE_SHORTCUTS) {
|
||||
logger.warn('shortcut disabled')
|
||||
return
|
||||
}
|
||||
|
||||
triggerHook('GLOBAL_KEYDOWN', e)
|
||||
|
||||
for (const action of Object.values(actions)) {
|
||||
if (isAction(e, action)) {
|
||||
if (action.when && !action.when()) {
|
||||
continue
|
||||
}
|
||||
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
getActionHandler(action.name)()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function keyupHandler (e: KeyboardEvent) {
|
||||
recordKeys(e)
|
||||
triggerHook('GLOBAL_KEYUP', e)
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', keydownHandler, true)
|
||||
window.addEventListener('keyup', keyupHandler, true)
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
import { upperFirst } from 'lodash-es'
|
||||
import { getLogger } from '@fe/utils'
|
||||
import { isMacOS } from '@fe/support/env'
|
||||
import { FLAG_DISABLE_SHORTCUTS } from '@fe/support/args'
|
||||
import { getActionHandler } from './action'
|
||||
import { triggerHook } from './hook'
|
||||
|
||||
const logger = getLogger('command')
|
||||
|
||||
export const Escape = 'Escape'
|
||||
export const Ctrl = 'Ctrl'
|
||||
export const Meta = 'Meta'
|
||||
export const CtrlCmd = 'CtrlCmd'
|
||||
export const Alt = 'Alt'
|
||||
export const Space = 'Space'
|
||||
export const Shift = 'Shift'
|
||||
export const BracketLeft = 'BracketLeft'
|
||||
export const BracketRight = 'BracketRight'
|
||||
export const LeftClick = 0
|
||||
export const Tab = 'Tab'
|
||||
|
||||
type XKey = typeof Ctrl | typeof CtrlCmd | typeof Alt | typeof Shift
|
||||
|
||||
export interface Command {
|
||||
/**
|
||||
* Command Id
|
||||
*/
|
||||
id: string,
|
||||
|
||||
/**
|
||||
* Associate shortcuts
|
||||
*/
|
||||
keys: null | (string | number)[]
|
||||
|
||||
/**
|
||||
* Handler
|
||||
*/
|
||||
handler: null | string | (() => void),
|
||||
|
||||
/**
|
||||
* When should execute handler
|
||||
*/
|
||||
when?: () => boolean
|
||||
}
|
||||
|
||||
const commands: { [key: string]: Command } = {}
|
||||
|
||||
let keys: Record<string, boolean> = {}
|
||||
|
||||
function recordKeys (e: KeyboardEvent) {
|
||||
if (e.type === 'keydown') {
|
||||
keys[e.key.toUpperCase()] = true
|
||||
} else {
|
||||
// keyup event not fired some times such as in key combination.
|
||||
keys = {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user has pressed the key
|
||||
* @param key upper case.
|
||||
*/
|
||||
export function isKeydown (key: string) {
|
||||
return !!keys[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user has pressed the Cmd key (macOS) or Ctrl key.
|
||||
* @param e
|
||||
* @returns
|
||||
*/
|
||||
export function hasCtrlCmd (e: KeyboardEvent | MouseEvent) {
|
||||
return isMacOS ? e.metaKey : e.ctrlKey
|
||||
}
|
||||
|
||||
/**
|
||||
* Get key label.
|
||||
* @param key
|
||||
* @returns
|
||||
*/
|
||||
export function getKeyLabel (key: XKey | string | number) {
|
||||
const str = {
|
||||
CtrlCmd: isMacOS ? '⌘' : 'Ctrl',
|
||||
Alt: isMacOS ? '⌥' : 'Alt',
|
||||
Ctrl: isMacOS ? '⌃' : 'Ctrl',
|
||||
Shift: isMacOS ? '⇧' : 'Shift',
|
||||
BracketLeft: '[',
|
||||
BracketRight: ']',
|
||||
Period: '.',
|
||||
Tab: 'Tab',
|
||||
}[key]
|
||||
|
||||
return str || upperFirst(key.toString())
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the event matches the shortcut key combination.
|
||||
* @param e
|
||||
* @param keys
|
||||
* @returns
|
||||
*/
|
||||
export function matchKeys (e: KeyboardEvent | MouseEvent, keys: (string | number)[]) {
|
||||
const modifiers = { metaKey: false, ctrlKey: false, altKey: false, shiftKey: false }
|
||||
|
||||
for (const key of keys) {
|
||||
switch (key) {
|
||||
case CtrlCmd:
|
||||
if (isMacOS) {
|
||||
modifiers.metaKey = true
|
||||
} else {
|
||||
modifiers.ctrlKey = true
|
||||
}
|
||||
if (!hasCtrlCmd(e)) return false
|
||||
break
|
||||
case Alt:
|
||||
modifiers.altKey = true
|
||||
if (!e.altKey) return false
|
||||
break
|
||||
case Ctrl:
|
||||
modifiers.ctrlKey = true
|
||||
if (!e.ctrlKey) return false
|
||||
break
|
||||
case Meta:
|
||||
modifiers.metaKey = true
|
||||
if (!e.ctrlKey) return false
|
||||
break
|
||||
case Shift:
|
||||
modifiers.shiftKey = true
|
||||
if (!e.shiftKey) return false
|
||||
break
|
||||
default:
|
||||
// if the event from iframe, it not instance of KeyboardEvent.
|
||||
if (e instanceof KeyboardEvent || '' + e === '[object KeyboardEvent]') {
|
||||
e = e as KeyboardEvent
|
||||
if (
|
||||
key !== e.key &&
|
||||
key.toString().toUpperCase() !== e.code.toUpperCase() &&
|
||||
`Key${key}`.toUpperCase() !== e.code.toUpperCase()
|
||||
) return false
|
||||
} else {
|
||||
if (key !== e.button) return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modifiers.altKey === e.altKey &&
|
||||
modifiers.ctrlKey === e.ctrlKey &&
|
||||
modifiers.metaKey === e.metaKey &&
|
||||
modifiers.shiftKey === e.shiftKey
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a command
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
export function getCommand (id: string): Command | undefined {
|
||||
return commands[id]
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the event shortcut key combination matches a command.
|
||||
* @param e
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
export function isCommand (e: KeyboardEvent | MouseEvent, id: string) {
|
||||
const command = getCommand(id)
|
||||
return !!(command && command.keys && matchKeys(e, command.keys))
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a command
|
||||
* @param command
|
||||
* @returns
|
||||
*/
|
||||
export function runCommand (command: Command) {
|
||||
if (typeof command.handler === 'string') {
|
||||
return getActionHandler(command.handler)()
|
||||
} else {
|
||||
return command.handler?.()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shortcuts label.
|
||||
* @param idOrKeys command id or keys
|
||||
* @returns
|
||||
*/
|
||||
export function getKeysLabel (id: string): string
|
||||
export function getKeysLabel (keys: (string | number)[]): string
|
||||
export function getKeysLabel (idOrKeys: (string | number)[] | string): string {
|
||||
let keys = []
|
||||
|
||||
if (typeof idOrKeys === 'string') {
|
||||
const command = getCommand(idOrKeys)
|
||||
if (!command || !command.keys) {
|
||||
return ''
|
||||
}
|
||||
|
||||
keys = command.keys
|
||||
} else {
|
||||
keys = idOrKeys
|
||||
}
|
||||
|
||||
return keys.map(getKeyLabel).join(isMacOS ? ' ' : '+')
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a command.
|
||||
* @param command
|
||||
* @returns
|
||||
*/
|
||||
export function registerCommand (command: Command) {
|
||||
logger.debug('registerCommand', command)
|
||||
commands[command.id] = command
|
||||
return command
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a command
|
||||
* @param id
|
||||
*/
|
||||
export function removeCommand (id: string) {
|
||||
logger.debug('removeCommand', id)
|
||||
delete commands[id]
|
||||
}
|
||||
|
||||
export function keydownHandler (e: KeyboardEvent) {
|
||||
recordKeys(e)
|
||||
|
||||
if (FLAG_DISABLE_SHORTCUTS) {
|
||||
logger.warn('shortcut disabled')
|
||||
return
|
||||
}
|
||||
|
||||
triggerHook('GLOBAL_KEYDOWN', e)
|
||||
|
||||
for (const command of Object.values(commands)) {
|
||||
if (isCommand(e, command.id)) {
|
||||
if (command.when && !command.when()) {
|
||||
continue
|
||||
}
|
||||
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
runCommand(command)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function keyupHandler (e: KeyboardEvent) {
|
||||
recordKeys(e)
|
||||
triggerHook('GLOBAL_KEYUP', e)
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', keydownHandler, true)
|
||||
window.addEventListener('keyup', keyupHandler, true)
|
||||
@@ -57,10 +57,10 @@
|
||||
if (window.parent && window.parent.ctx && params.get('trigger-parent-keyboard-event') === 'true') {
|
||||
window.addEventListener('keydown', (e) => {
|
||||
// debugger
|
||||
window.parent.ctx.command.keydownHandler(e)
|
||||
window.parent.ctx.action.keydownHandler(e)
|
||||
}, true)
|
||||
window.addEventListener('keyup', (e) => {
|
||||
window.parent.ctx.command.keyupHandler(e)
|
||||
window.parent.ctx.action.keyupHandler(e)
|
||||
}, true)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export default {
|
||||
{
|
||||
type: 'btn',
|
||||
icon: 'side-bar',
|
||||
title: ctx.i18n.t('control-center.switch.side-bar', ctx.command.getKeysLabel('layout.toggle-side')),
|
||||
title: ctx.i18n.t('control-center.switch.side-bar', ctx.action.getKeysLabel('layout.toggle-side')),
|
||||
checked: ctx.store.state.showSide,
|
||||
onClick: () => {
|
||||
ctx.layout.toggleSide()
|
||||
@@ -21,7 +21,7 @@ export default {
|
||||
{
|
||||
type: 'btn',
|
||||
icon: 'edit-solid',
|
||||
title: ctx.i18n.t('control-center.switch.editor', ctx.command.getKeysLabel('layout.toggle-editor')),
|
||||
title: ctx.i18n.t('control-center.switch.editor', ctx.action.getKeysLabel('layout.toggle-editor')),
|
||||
checked: ctx.store.state.showEditor,
|
||||
onClick: () => {
|
||||
ctx.layout.toggleEditor()
|
||||
@@ -30,7 +30,7 @@ export default {
|
||||
{
|
||||
type: 'btn',
|
||||
icon: 'eye-regular',
|
||||
title: ctx.i18n.t('control-center.switch.view', ctx.command.getKeysLabel('layout.toggle-view')),
|
||||
title: ctx.i18n.t('control-center.switch.view', ctx.action.getKeysLabel('layout.toggle-view')),
|
||||
checked: ctx.store.state.showView,
|
||||
onClick: () => {
|
||||
ctx.layout.toggleView()
|
||||
@@ -60,7 +60,7 @@ export default {
|
||||
{
|
||||
type: 'btn',
|
||||
icon: 'text-width-solid',
|
||||
title: ctx.i18n.t('control-center.switch.word-wrap', ctx.command.getKeysLabel('editor.toggle-wrap')),
|
||||
title: ctx.i18n.t('control-center.switch.word-wrap', ctx.action.getKeysLabel('editor.toggle-wrap')),
|
||||
checked: ctx.store.state.wordWrap === 'on',
|
||||
onClick: () => {
|
||||
ctx.editor.toggleWrap()
|
||||
@@ -90,7 +90,7 @@ export default {
|
||||
type: 'btn',
|
||||
icon: 'sync-alt-solid',
|
||||
flat: true,
|
||||
title: ctx.i18n.t('control-center.navigation.refresh', ctx.command.getKeysLabel('view.refresh')),
|
||||
title: ctx.i18n.t('control-center.navigation.refresh', ctx.action.getKeysLabel('view.refresh')),
|
||||
onClick: () => {
|
||||
ctx.view.refresh()
|
||||
ctx.workbench.ControlCenter.toggle(false)
|
||||
@@ -100,7 +100,7 @@ export default {
|
||||
type: 'btn',
|
||||
icon: 'bolt-solid',
|
||||
flat: true,
|
||||
title: ctx.i18n.t('control-center.navigation.goto', ctx.command.getKeysLabel('filter.show-quick-open')),
|
||||
title: ctx.i18n.t('control-center.navigation.goto', ctx.action.getKeysLabel('filter.show-quick-open')),
|
||||
showInActionBar: true,
|
||||
onClick: () => {
|
||||
ctx.action.getActionHandler('filter.show-quick-open')()
|
||||
|
||||
@@ -128,7 +128,7 @@ export default {
|
||||
ctx.action.registerAction({
|
||||
name: id,
|
||||
handler: copyContent,
|
||||
keys: [ctx.command.CtrlCmd, ctx.command.Shift, 'c'],
|
||||
keys: [ctx.action.Keys.CtrlCmd, ctx.action.Keys.Shift, 'c'],
|
||||
})
|
||||
|
||||
ctx.statusBar.tapMenus(menus => {
|
||||
@@ -137,7 +137,7 @@ export default {
|
||||
id,
|
||||
type: 'normal',
|
||||
title: ctx.i18n.t('status-bar.tool.copy-content'),
|
||||
subTitle: ctx.command.getKeysLabel(id),
|
||||
subTitle: ctx.action.getKeysLabel(id),
|
||||
onClick: () => copyContent(),
|
||||
order: 100,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Plugin } from '@fe/context'
|
||||
import { hasCtrlCmd } from '@fe/core/command'
|
||||
import { copyText, encodeMarkdownLink } from '@fe/utils'
|
||||
import store from '@fe/support/store'
|
||||
|
||||
@@ -16,7 +15,7 @@ export default {
|
||||
}
|
||||
|
||||
// copy heading link.
|
||||
if (['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].indexOf(target.tagName) > -1 && target.id && hasCtrlCmd(e)) {
|
||||
if (['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].indexOf(target.tagName) > -1 && target.id && ctx.action.hasCtrlCmd(e)) {
|
||||
const { currentFile } = store.state
|
||||
if (currentFile) {
|
||||
let path = currentFile.path
|
||||
@@ -35,7 +34,7 @@ export default {
|
||||
}
|
||||
|
||||
// copy content.
|
||||
if (target.classList.contains('copy-inner-text') && hasCtrlCmd(e)) {
|
||||
if (target.classList.contains('copy-inner-text') && ctx.action.hasCtrlCmd(e)) {
|
||||
copyText(target.innerText)
|
||||
return preventEvent()
|
||||
}
|
||||
|
||||
@@ -53,13 +53,13 @@ export default {
|
||||
ctx.action.registerAction({
|
||||
name: backId,
|
||||
handler: () => go(-1),
|
||||
keys: [ctx.command.Alt, ctx.command.BracketLeft],
|
||||
keys: [ctx.action.Keys.Alt, ctx.action.Keys.BracketLeft],
|
||||
})
|
||||
|
||||
ctx.action.registerAction({
|
||||
name: forwardId,
|
||||
handler: () => go(1),
|
||||
keys: [ctx.command.Alt, ctx.command.BracketRight],
|
||||
keys: [ctx.action.Keys.Alt, ctx.action.Keys.BracketRight],
|
||||
})
|
||||
|
||||
ctx.registerHook('STARTUP', () => {
|
||||
@@ -70,7 +70,7 @@ export default {
|
||||
type: 'normal' as any,
|
||||
title: ctx.i18n.t('status-bar.nav.forward'),
|
||||
disabled: idx >= stack.length - 1,
|
||||
subTitle: ctx.command.getKeysLabel(forwardId),
|
||||
subTitle: ctx.action.getKeysLabel(forwardId),
|
||||
onClick: () => ctx.action.getActionHandler(forwardId)()
|
||||
},
|
||||
{
|
||||
@@ -78,7 +78,7 @@ export default {
|
||||
type: 'normal' as any,
|
||||
title: ctx.i18n.t('status-bar.nav.back'),
|
||||
disabled: idx <= 0,
|
||||
subTitle: ctx.command.getKeysLabel(backId),
|
||||
subTitle: ctx.action.getKeysLabel(backId),
|
||||
onClick: () => ctx.action.getActionHandler(backId)()
|
||||
},
|
||||
)
|
||||
@@ -91,7 +91,7 @@ export default {
|
||||
type: 'btn',
|
||||
icon: 'arrow-left-solid',
|
||||
flat: true,
|
||||
title: ctx.i18n.t('control-center.navigation.back', ctx.command.getKeysLabel(backId)),
|
||||
title: ctx.i18n.t('control-center.navigation.back', ctx.action.getKeysLabel(backId)),
|
||||
disabled: idx <= 0,
|
||||
showInActionBar: true,
|
||||
onClick: () => ctx.action.getActionHandler(backId)()
|
||||
@@ -100,7 +100,7 @@ export default {
|
||||
type: 'btn',
|
||||
icon: 'arrow-right-solid',
|
||||
flat: true,
|
||||
title: ctx.i18n.t('control-center.navigation.forward', ctx.command.getKeysLabel(forwardId)),
|
||||
title: ctx.i18n.t('control-center.navigation.forward', ctx.action.getKeysLabel(forwardId)),
|
||||
disabled: idx >= stack.length - 1,
|
||||
showInActionBar: true,
|
||||
onClick: () => ctx.action.getActionHandler(forwardId)()
|
||||
|
||||
@@ -120,28 +120,28 @@ export default {
|
||||
id: 'plugin.editor.add-image',
|
||||
type: 'normal',
|
||||
title: ctx.i18n.t('add-image'),
|
||||
subTitle: ctx.command.getKeysLabel([ctx.command.Alt, 'i']),
|
||||
subTitle: ctx.action.getKeysLabel([ctx.action.Keys.Alt, 'i']),
|
||||
onClick: () => addAttachment(true),
|
||||
},
|
||||
{
|
||||
id: 'plugin.editor.add-file',
|
||||
type: 'normal',
|
||||
title: ctx.i18n.t('editor.context-menu.add-attachment'),
|
||||
subTitle: ctx.command.getKeysLabel([ctx.command.Alt, 'f']),
|
||||
subTitle: ctx.action.getKeysLabel([ctx.action.Keys.Alt, 'f']),
|
||||
onClick: () => addAttachment(false),
|
||||
},
|
||||
{
|
||||
id: 'plugin.editor.link-doc',
|
||||
type: 'normal',
|
||||
title: ctx.i18n.t('editor.context-menu.link-doc'),
|
||||
subTitle: ctx.command.getKeysLabel([ctx.command.Alt, 'd']),
|
||||
subTitle: ctx.action.getKeysLabel([ctx.action.Keys.Alt, 'd']),
|
||||
onClick: () => getActionHandler('filter.choose-document')().then(addDocument),
|
||||
},
|
||||
{
|
||||
id: 'plugin.editor.link-file',
|
||||
type: 'normal',
|
||||
title: ctx.i18n.t('editor.context-menu.link-file'),
|
||||
subTitle: ctx.command.getKeysLabel([ctx.command.Alt, ctx.command.Shift, 'f']),
|
||||
subTitle: ctx.action.getKeysLabel([ctx.action.Keys.Alt, ctx.action.Keys.Shift, 'f']),
|
||||
onClick: () => linkFile(),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
|
||||
@@ -5,7 +5,7 @@ import { deleteLine, getEditor, getLineContent, getOneIndent, insert, replaceLin
|
||||
import type { Plugin } from '@fe/context'
|
||||
import { t } from '@fe/services/i18n'
|
||||
import { getSetting } from '@fe/services/setting'
|
||||
import { isKeydown } from '@fe/core/command'
|
||||
import { isKeydown, Keys } from '@fe/core/action'
|
||||
|
||||
function processCursorChange (source: string, position: Monaco.Position) {
|
||||
const isEnter = source === 'keyboard' && isKeydown('ENTER')
|
||||
@@ -167,14 +167,14 @@ export default {
|
||||
id: 'plugin.editor.insert-time',
|
||||
type: 'normal',
|
||||
title: ctx.i18n.t('editor.context-menu.insert-time'),
|
||||
subTitle: ctx.command.getKeysLabel([ctx.command.Shift, ctx.command.Alt, 't']),
|
||||
subTitle: ctx.action.getKeysLabel([Keys.Shift, Keys.Alt, 't']),
|
||||
onClick: insertTime,
|
||||
},
|
||||
{
|
||||
id: 'plugin.editor.insert-date',
|
||||
type: 'normal',
|
||||
title: ctx.i18n.t('editor.context-menu.insert-date'),
|
||||
subTitle: ctx.command.getKeysLabel([ctx.command.Shift, ctx.command.Alt, 'd']),
|
||||
subTitle: ctx.action.getKeysLabel([Keys.Shift, Keys.Alt, 'd']),
|
||||
onClick: insertDate,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import { refreshTree } from '@fe/services/tree'
|
||||
import { upload } from '@fe/services/base'
|
||||
import store from '@fe/support/store'
|
||||
import { encodeMarkdownLink, fileToBase64URL, path } from '@fe/utils'
|
||||
import { isKeydown } from '@fe/core/command'
|
||||
import { isKeydown } from '@fe/core/action'
|
||||
|
||||
const IMAGE_REG = /^image\/(png|jpg|jpeg|gif)$/i
|
||||
const HTML_REG = /^text\/html$/i
|
||||
|
||||
@@ -31,19 +31,19 @@ export default {
|
||||
|
||||
ctx.action.registerAction({
|
||||
name: zoomInId,
|
||||
keys: [ctx.command.CtrlCmd, '='],
|
||||
keys: [ctx.action.Keys.CtrlCmd, '='],
|
||||
handler: () => changeZoomFactor(true)
|
||||
})
|
||||
|
||||
ctx.action.registerAction({
|
||||
name: zoomOutId,
|
||||
keys: [ctx.command.CtrlCmd, '-'],
|
||||
keys: [ctx.action.Keys.CtrlCmd, '-'],
|
||||
handler: () => changeZoomFactor(false)
|
||||
})
|
||||
|
||||
ctx.action.registerAction({
|
||||
name: zoomResetId,
|
||||
keys: [ctx.command.CtrlCmd, '0'],
|
||||
keys: [ctx.action.Keys.CtrlCmd, '0'],
|
||||
handler: () => setZoomFactor(1)
|
||||
})
|
||||
|
||||
@@ -56,7 +56,7 @@ export default {
|
||||
type: 'normal' as any,
|
||||
title: ctx.i18n.t('electron-zoom.zoom-in'),
|
||||
disabled: factor >= 1.9,
|
||||
subTitle: ctx.command.getKeysLabel(zoomInId),
|
||||
subTitle: ctx.action.getKeysLabel(zoomInId),
|
||||
onClick: () => ctx.action.getActionHandler(zoomInId)()
|
||||
},
|
||||
{
|
||||
@@ -64,7 +64,7 @@ export default {
|
||||
type: 'normal' as any,
|
||||
title: ctx.i18n.t('electron-zoom.zoom-out'),
|
||||
disabled: factor < 0.25,
|
||||
subTitle: ctx.command.getKeysLabel(zoomOutId),
|
||||
subTitle: ctx.action.getKeysLabel(zoomOutId),
|
||||
onClick: () => ctx.action.getActionHandler(zoomOutId)()
|
||||
},
|
||||
{
|
||||
@@ -72,7 +72,7 @@ export default {
|
||||
type: 'normal' as any,
|
||||
title: ctx.i18n.t('electron-zoom.zoom-reset'),
|
||||
disabled: factor === 1,
|
||||
subTitle: ctx.command.getKeysLabel(zoomResetId),
|
||||
subTitle: ctx.action.getKeysLabel(zoomResetId),
|
||||
onClick: () => ctx.action.getActionHandler(zoomResetId)()
|
||||
},
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as api from '@fe/support/api'
|
||||
import { encodeMarkdownLink } from '@fe/utils'
|
||||
import { useToast } from '@fe/support/ui/toast'
|
||||
import store from '@fe/support/store'
|
||||
import { CtrlCmd, isCommand, LeftClick, Shift } from '@fe/core/command'
|
||||
import { Keys, matchKeys } from '@fe/core/action'
|
||||
import { replaceValue } from '@fe/services/editor'
|
||||
import { refreshTree } from '@fe/services/tree'
|
||||
import { upload } from '@fe/services/base'
|
||||
@@ -88,7 +88,7 @@ async function handleClick ({ e }: { e: MouseEvent }) {
|
||||
}
|
||||
|
||||
const img = target as HTMLImageElement
|
||||
if (isCommand(e, commandClick)) { // download image to local
|
||||
if (matchKeys(e, [Keys.CtrlCmd, Keys.Shift, Keys.LeftClick])) { // download image to local
|
||||
const data = await transformImgOutLink(img)
|
||||
if (data) {
|
||||
replaceValue(data.oldLink, data.replacedLink)
|
||||
@@ -112,12 +112,6 @@ export default {
|
||||
handler: transformAll
|
||||
})
|
||||
|
||||
ctx.command.registerCommand({
|
||||
id: commandClick,
|
||||
keys: [CtrlCmd, Shift, LeftClick],
|
||||
handler: null
|
||||
})
|
||||
|
||||
ctx.registerHook('VIEW_ELEMENT_CLICK', handleClick)
|
||||
|
||||
ctx.statusBar.tapMenus(menus => {
|
||||
|
||||
@@ -73,7 +73,7 @@ export default {
|
||||
const codeInline = (fn: Function) => (tokens: any, idx: any, options: any, env: any, slf: any) => {
|
||||
if (tokens[idx].attrIndex('title') < 0) {
|
||||
tokens[idx].attrJoin('class', ctx.args.DOM_CLASS_NAME.COPY_INNER_TEXT)
|
||||
tokens[idx].attrPush(['title', ctx.command.getKeyLabel('CtrlCmd') + ' + ' + ctx.i18n.t('click-to-copy')])
|
||||
tokens[idx].attrPush(['title', ctx.action.getKeyLabel('CtrlCmd') + ' + ' + ctx.i18n.t('click-to-copy')])
|
||||
}
|
||||
|
||||
return (fn)(tokens, idx, options, env, slf)
|
||||
|
||||
@@ -2,9 +2,8 @@ import { computed, defineComponent, getCurrentInstance, h, onBeforeUnmount, ref,
|
||||
import Markdown from 'markdown-it'
|
||||
import { escape } from 'lodash-es'
|
||||
import { Plugin } from '@fe/context'
|
||||
import { getActionHandler } from '@fe/core/action'
|
||||
import { getActionHandler, getKeyLabel, Keys, matchKeys } from '@fe/core/action'
|
||||
import { FLAG_DISABLE_XTERM } from '@fe/support/args'
|
||||
import { CtrlCmd, getKeyLabel, matchKeys } from '@fe/core/command'
|
||||
import { useI18n } from '@fe/services/i18n'
|
||||
import { getLogger, md5 } from '@fe/utils'
|
||||
import SvgIcon from '@fe/components/SvgIcon.vue'
|
||||
@@ -113,7 +112,7 @@ const RunCode = defineComponent({
|
||||
getActionHandler('xterm.run')({
|
||||
code: props.code,
|
||||
start: cmd.start,
|
||||
exit: matchKeys(e, [CtrlCmd]) ? undefined : cmd.exit,
|
||||
exit: matchKeys(e, [Keys.CtrlCmd]) ? undefined : cmd.exit,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -151,7 +150,7 @@ const RunCode = defineComponent({
|
||||
onClick: run
|
||||
}),
|
||||
h('div', {
|
||||
title: t('code-run.run-in-xterm-tips', getKeyLabel(CtrlCmd)),
|
||||
title: t('code-run.run-in-xterm-tips', getKeyLabel(Keys.CtrlCmd)),
|
||||
class: 'p-mcr-run-xterm-btn skip-print',
|
||||
hidden: !getTerminalCmd.value,
|
||||
onClick: runInXterm
|
||||
|
||||
@@ -3,7 +3,7 @@ import Sortable from 'sortablejs'
|
||||
import { orderBy } from 'lodash-es'
|
||||
import type { Plugin } from '@fe/context'
|
||||
import { useModal } from '@fe/support/ui/modal'
|
||||
import { hasCtrlCmd } from '@fe/core/command'
|
||||
import { hasCtrlCmd } from '@fe/core/action'
|
||||
import { DOM_ATTR_NAME, FLAG_READONLY } from '@fe/support/args'
|
||||
import { useToast } from '@fe/support/ui/toast'
|
||||
import { disableSyncScrollAwhile, renderImmediately } from '@fe/services/view'
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import { h } from 'vue'
|
||||
import Markdown from 'markdown-it'
|
||||
import StateInline from 'markdown-it/lib/rules_inline/state_inline'
|
||||
import { getKeyLabel } from '@fe/core/command'
|
||||
import ctx, { Plugin } from '@fe/context'
|
||||
|
||||
const slugify = (s: string) => encodeURIComponent(String(s).trim().toUpperCase().replace(/\s+/g, '-'))
|
||||
@@ -166,7 +165,7 @@ const MarkdownItPlugin = (md: Markdown, o: any) => {
|
||||
return h('div', { key: html, class: options.containerClass, innerHTML: html }) as any
|
||||
}
|
||||
|
||||
const headTitle = getKeyLabel('CtrlCmd') + ' + ' + ctx.i18n.t('click-to-copy-link')
|
||||
const headTitle = ctx.action.getKeyLabel(ctx.action.Keys.CtrlCmd) + ' + ' + ctx.i18n.t('click-to-copy-link')
|
||||
|
||||
md.renderer.rules.heading_open = function (tokens, idx, opt, env, slf) {
|
||||
const header = tokens[idx]
|
||||
|
||||
@@ -12,7 +12,7 @@ export default {
|
||||
menus['status-bar-history'] = {
|
||||
id: 'status-bar-history',
|
||||
position: 'right',
|
||||
tips: ctx.i18n.t('status-bar.tool.doc-history') + ` (${ctx.command.getKeysLabel(action.name)})`,
|
||||
tips: ctx.i18n.t('status-bar.tool.doc-history') + ` (${ctx.action.getKeysLabel(action.name)})`,
|
||||
icon: 'history-solid',
|
||||
onClick: () => {
|
||||
action.handler()
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
id: 'show-quick-open',
|
||||
type: 'normal',
|
||||
title: ctx.i18n.t('status-bar.nav.goto'),
|
||||
subTitle: ctx.command.getKeysLabel('filter.show-quick-open'),
|
||||
subTitle: ctx.action.getKeysLabel('filter.show-quick-open'),
|
||||
onClick: () => ctx.action.getActionHandler('filter.show-quick-open')()
|
||||
},
|
||||
{ type: 'separator' },
|
||||
|
||||
@@ -9,7 +9,7 @@ export default {
|
||||
menus['status-bar-presentation'] = {
|
||||
id: 'status-bar-presentation',
|
||||
position: 'right',
|
||||
tips: ctx.i18n.t('status-bar.present') + ` (${ctx.command.getKeysLabel(action!.name)})`,
|
||||
tips: ctx.i18n.t('status-bar.present') + ` (${ctx.action.getKeysLabel(action!.name)})`,
|
||||
icon: 'presentation',
|
||||
onClick: () => {
|
||||
ctx.view.enterPresent()
|
||||
|
||||
@@ -58,10 +58,10 @@ export default {
|
||||
checked: currentRepo && currentRepo.name === name && currentRepo.path === path,
|
||||
onClick: () => choose({ name, path }),
|
||||
subTitle: i === arr.length - 1
|
||||
? ctx.command.getKeysLabel([ctx.command.Alt, '0'])
|
||||
? ctx.action.getKeysLabel([ctx.action.Keys.Alt, '0'])
|
||||
: (
|
||||
i < 9
|
||||
? ctx.command.getKeysLabel([ctx.command.Alt, (i + 1).toString()])
|
||||
? ctx.action.getKeysLabel([ctx.action.Keys.Alt, (i + 1).toString()])
|
||||
: undefined
|
||||
),
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export default {
|
||||
menus['status-bar-terminal'] = {
|
||||
id: 'status-bar-terminal',
|
||||
position: 'right',
|
||||
tips: ctx.i18n.t('status-bar.terminal') + ` (${ctx.command.getKeysLabel('layout.toggle-xterm')})`,
|
||||
tips: ctx.i18n.t('status-bar.terminal') + ` (${ctx.action.getKeysLabel('layout.toggle-xterm')})`,
|
||||
icon: 'terminal',
|
||||
onClick: () => ctx.action.getActionHandler('layout.toggle-xterm')()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { Plugin } from '@fe/context'
|
||||
import { FLAG_DISABLE_XTERM } from '@fe/support/args'
|
||||
import { getKeysLabel } from '@fe/core/command'
|
||||
|
||||
export default {
|
||||
name: 'status-bar-view',
|
||||
@@ -24,7 +23,7 @@ export default {
|
||||
type: 'normal',
|
||||
checked: ctx.store.state.wordWrap === 'on',
|
||||
title: ctx.i18n.t('status-bar.view.word-wrap'),
|
||||
subTitle: getKeysLabel('editor.toggle-wrap'),
|
||||
subTitle: ctx.action.getKeysLabel('editor.toggle-wrap'),
|
||||
onClick: () => ctx.editor.toggleWrap()
|
||||
},
|
||||
{
|
||||
@@ -40,7 +39,7 @@ export default {
|
||||
type: 'normal',
|
||||
title: ctx.i18n.t('status-bar.view.side-bar'),
|
||||
checked: ctx.store.state.showSide,
|
||||
subTitle: getKeysLabel('layout.toggle-side'),
|
||||
subTitle: ctx.action.getKeysLabel('layout.toggle-side'),
|
||||
onClick: () => ctx.layout.toggleSide()
|
||||
},
|
||||
{
|
||||
@@ -48,7 +47,7 @@ export default {
|
||||
type: 'normal',
|
||||
checked: ctx.store.state.showEditor,
|
||||
title: ctx.i18n.t('status-bar.view.editor'),
|
||||
subTitle: getKeysLabel('layout.toggle-editor'),
|
||||
subTitle: ctx.action.getKeysLabel('layout.toggle-editor'),
|
||||
onClick: () => ctx.layout.toggleEditor()
|
||||
},
|
||||
{
|
||||
@@ -56,7 +55,7 @@ export default {
|
||||
type: 'normal',
|
||||
checked: ctx.store.state.showView,
|
||||
title: ctx.i18n.t('status-bar.view.preview'),
|
||||
subTitle: getKeysLabel('layout.toggle-view'),
|
||||
subTitle: ctx.action.getKeysLabel('layout.toggle-view'),
|
||||
onClick: () => ctx.layout.toggleView()
|
||||
},
|
||||
...(!FLAG_DISABLE_XTERM ? [{
|
||||
@@ -64,7 +63,7 @@ export default {
|
||||
type: 'normal' as any,
|
||||
checked: ctx.store.state.showXterm,
|
||||
title: ctx.i18n.t('status-bar.view.xterm'),
|
||||
subTitle: getKeysLabel('layout.toggle-xterm'),
|
||||
subTitle: ctx.action.getKeysLabel('layout.toggle-xterm'),
|
||||
onClick: () => ctx.layout.toggleXterm()
|
||||
}] : []),
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ export default {
|
||||
|
||||
ctx.action.registerAction({
|
||||
name: actionName,
|
||||
keys: [ctx.command.Alt, 'o'],
|
||||
keys: [ctx.action.Keys.Alt, 'o'],
|
||||
handler: (line?: number, checked?: boolean) => {
|
||||
if (line) {
|
||||
switchTodo(line, checked)
|
||||
|
||||
@@ -3,9 +3,8 @@ import { cloneDeep, debounce } from 'lodash-es'
|
||||
import { FLAG_READONLY } from '@fe/support/args'
|
||||
import { isElectron, isMacOS } from '@fe/support/env'
|
||||
import { registerHook, triggerHook } from '@fe/core/hook'
|
||||
import { getActionHandler, registerAction } from '@fe/core/action'
|
||||
import { getActionHandler, Keys, registerAction } from '@fe/core/action'
|
||||
import * as ioc from '@fe/core/ioc'
|
||||
import { Alt } from '@fe/core/command'
|
||||
import store from '@fe/support/store'
|
||||
import { useToast } from '@fe/support/ui/toast'
|
||||
import { sleep } from '@fe/utils'
|
||||
@@ -443,7 +442,7 @@ export function getIsDefault () {
|
||||
return isDefaultEditor
|
||||
}
|
||||
|
||||
registerAction({ name: 'editor.toggle-wrap', handler: toggleWrap, keys: [Alt, 'w'] })
|
||||
registerAction({ name: 'editor.toggle-wrap', handler: toggleWrap, keys: [Keys.Alt, 'w'] })
|
||||
|
||||
registerHook('EDITOR_CURRENT_EDITOR_CHANGE', ({ current }) => {
|
||||
isDefaultEditor = !current?.component
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { nextTick } from 'vue'
|
||||
import { throttle } from 'lodash-es'
|
||||
import { triggerHook } from '@fe/core/hook'
|
||||
import { getActionHandler, registerAction } from '@fe/core/action'
|
||||
import { Alt } from '@fe/core/command'
|
||||
import { getActionHandler, Keys, registerAction } from '@fe/core/action'
|
||||
import store from '@fe/support/store'
|
||||
import * as view from './view'
|
||||
|
||||
@@ -97,7 +96,7 @@ export function toggleEditorPreviewExclusive (exclusive?: boolean) {
|
||||
emitResize()
|
||||
}
|
||||
|
||||
registerAction({ name: 'layout.toggle-side', handler: toggleSide, keys: [Alt, 'e'] })
|
||||
registerAction({ name: 'layout.toggle-editor', handler: toggleEditor, keys: [Alt, 'x'] })
|
||||
registerAction({ name: 'layout.toggle-view', handler: toggleView, keys: [Alt, 'v'] })
|
||||
registerAction({ name: 'layout.toggle-xterm', handler: toggleXterm, keys: [Alt, 't'] })
|
||||
registerAction({ name: 'layout.toggle-side', handler: toggleSide, keys: [Keys.Alt, 'e'] })
|
||||
registerAction({ name: 'layout.toggle-editor', handler: toggleEditor, keys: [Keys.Alt, 'x'] })
|
||||
registerAction({ name: 'layout.toggle-view', handler: toggleView, keys: [Keys.Alt, 'v'] })
|
||||
registerAction({ name: 'layout.toggle-xterm', handler: toggleXterm, keys: [Keys.Alt, 't'] })
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import juice from 'juice'
|
||||
import { CtrlCmd, Escape, registerCommand } from '@fe/core/command'
|
||||
import { getActionHandler, registerAction } from '@fe/core/action'
|
||||
import { getActionHandler, Keys, registerAction } from '@fe/core/action'
|
||||
import { registerHook, triggerHook } from '@fe/core/hook'
|
||||
import * as ioc from '@fe/core/ioc'
|
||||
import { DOM_ATTR_NAME, DOM_CLASS_NAME } from '@fe/support/args'
|
||||
@@ -62,7 +61,7 @@ export async function refresh () {
|
||||
await switchDoc({ type, name, path, repo }, true)
|
||||
}
|
||||
|
||||
getActionHandler('view.refresh')()
|
||||
getActionHandler('view.rerender')()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,7 +467,7 @@ registerAction({
|
||||
registerAction({
|
||||
name: 'view.exit-presentation',
|
||||
handler: () => present(false),
|
||||
keys: [Escape],
|
||||
keys: [Keys.Escape],
|
||||
when: () => {
|
||||
const el = (renderIframe?.contentDocument || window.document).activeElement
|
||||
return store.state.presentation &&
|
||||
@@ -480,8 +479,8 @@ registerAction({
|
||||
}
|
||||
})
|
||||
|
||||
registerCommand({
|
||||
id: 'view.refresh',
|
||||
registerAction({
|
||||
name: 'view.refresh',
|
||||
handler: refresh,
|
||||
keys: [CtrlCmd, 'r']
|
||||
keys: [Keys.CtrlCmd, 'r']
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { debounce } from 'lodash-es'
|
||||
import * as ioc from '@fe/core/ioc'
|
||||
import { getActionHandler, registerAction } from '@fe/core/action'
|
||||
import { Alt, Shift } from '@fe/core/command'
|
||||
import { getActionHandler, Keys, registerAction } from '@fe/core/action'
|
||||
import store from '@fe/support/store'
|
||||
import type { Components } from '@fe/types'
|
||||
|
||||
@@ -13,7 +12,7 @@ export function toggleOutline (visible?: boolean) {
|
||||
store.commit('setShowOutline', typeof visible === 'boolean' ? visible : !store.state.showOutline)
|
||||
}
|
||||
|
||||
registerAction({ name: 'workbench.toggle-outline', handler: toggleOutline, keys: [Shift, Alt, 'o'] })
|
||||
registerAction({ name: 'workbench.toggle-outline', handler: toggleOutline, keys: [Keys.Shift, Keys.Alt, 'o'] })
|
||||
|
||||
const _refreshTabsActionBtns = debounce(() => {
|
||||
getActionHandler('file-tabs.refresh-action-btns')()
|
||||
|
||||
@@ -272,6 +272,7 @@ export interface BuildInSettings {
|
||||
export type BuildInActions = {
|
||||
'view.render-immediately': () => void,
|
||||
'view.render': () => void,
|
||||
'view.rerender': () => void,
|
||||
'view.refresh': () => void,
|
||||
'view.reveal-line': (startLine: number) => Promise<HTMLElement | null>,
|
||||
'view.get-content-html': () => string,
|
||||
|
||||
Reference in New Issue
Block a user