Add extension management service

This commit is contained in:
2025-06-24 14:16:53 +08:00
parent ea025e3f5d
commit f3bcb87828
31 changed files with 1682 additions and 176 deletions

View File

@@ -1,98 +0,0 @@
import { EditorView, ViewPlugin, ViewUpdate } from '@codemirror/view';
import { DocumentService } from '../../../../bindings/voidraft/internal/services';
import { useDebounceFn } from '@vueuse/core';
// 定义自动保存配置选项
export interface AutoSaveOptions {
// 保存回调
onSave?: (success: boolean) => void;
// 内容变更延迟传递(毫秒)- 输入时不会立即发送,有一个小延迟,避免频繁调用后端
debounceDelay?: number;
}
/**
* 创建自动保存插件
*
* @param options 配置选项
* @returns EditorView.Plugin
*/
export function createAutoSavePlugin(options: AutoSaveOptions = {}) {
const {
onSave = () => {},
debounceDelay = 2000
} = options;
return ViewPlugin.fromClass(
class {
private isActive: boolean = true;
private isSaving: boolean = false;
private readonly contentUpdateFn: (view: EditorView) => void;
constructor(private view: EditorView) {
// 创建内容更新函数,简单传递内容给后端
this.contentUpdateFn = this.createDebouncedUpdateFn(debounceDelay);
}
/**
* 创建防抖的内容更新函数
*/
private createDebouncedUpdateFn(delay: number): (view: EditorView) => void {
// 使用VueUse的防抖函数创建一个新函数
return useDebounceFn(async (view: EditorView) => {
// 如果插件已不活跃或正在保存中,不发送
if (!this.isActive || this.isSaving) return;
this.isSaving = true;
const content = view.state.doc.toString();
try {
// 简单将内容传递给后端,让后端处理保存策略
await DocumentService.UpdateActiveDocumentContent(content);
onSave(true);
} catch (err) {
// 静默处理错误,不在控制台打印
onSave(false);
} finally {
this.isSaving = false;
}
}, delay);
}
update(update: ViewUpdate) {
// 如果内容没有变化,直接返回
if (!update.docChanged) return;
// 调用防抖函数
this.contentUpdateFn(this.view);
}
destroy() {
// 标记插件不再活跃
this.isActive = false;
// 静默发送最终内容,忽略错误
const content = this.view.state.doc.toString();
DocumentService.UpdateActiveDocumentContent(content).then();
}
}
);
}
/**
* 创建处理保存快捷键的插件
* @param onSave 保存回调
* @returns EditorView.Plugin
*/
export function createSaveShortcutPlugin(onSave: () => void) {
return EditorView.domEventHandlers({
keydown: (event) => {
// Ctrl+S / Cmd+S
if ((event.ctrlKey || event.metaKey) && event.key === 's') {
event.preventDefault();
onSave();
return true;
}
return false;
}
});
}

View File

@@ -1,95 +0,0 @@
import {Extension} from '@codemirror/state';
import {
crosshairCursor,
drawSelection,
dropCursor,
EditorView,
highlightActiveLine,
highlightActiveLineGutter,
highlightSpecialChars,
keymap,
lineNumbers,
rectangularSelection,
} from '@codemirror/view';
import {
bracketMatching,
defaultHighlightStyle,
foldGutter,
indentOnInput,
syntaxHighlighting,
} from '@codemirror/language';
import {history} from '@codemirror/commands';
import {highlightSelectionMatches} from '@codemirror/search';
import {autocompletion, closeBrackets, closeBracketsKeymap} from '@codemirror/autocomplete';
import {searchVisibilityField, vscodeSearch} from './vscodeSearch';
import {hyperLink} from './hyperlink';
import {color} from './colorSelector';
import {createTextHighlighter} from './textHighlightExtension';
import {minimap} from './minimap';
import {createCodeBlockExtension} from './codeblock/index';
import {foldingOnIndent} from './foldExtension'
import rainbowBrackets from "./rainbowBrackets";
import {createCodeBlastExtension} from './codeblast';
// 基本编辑器设置
export const createBasicSetup = (): Extension[] => {
return [
// 基础UI
lineNumbers(),
highlightActiveLineGutter(),
highlightSpecialChars(),
dropCursor(),
EditorView.lineWrapping,
// 历史记录
history(),
// 代码折叠
foldGutter(),
// 选择与高亮
drawSelection(),
highlightActiveLine(),
highlightSelectionMatches(),
rectangularSelection(),
crosshairCursor(),
// 缩进和编辑辅助
indentOnInput(),
syntaxHighlighting(defaultHighlightStyle, {fallback: true}),
bracketMatching(),
closeBrackets(),
// 自动完成
autocompletion(),
vscodeSearch,
searchVisibilityField,
foldingOnIndent,
rainbowBrackets(),
createCodeBlastExtension({
effect: 1,
shake: true,
maxParticles: 300,
shakeIntensity: 3
}),
hyperLink,
color,
...createTextHighlighter('hl'),
minimap({
displayText: 'characters',
showOverlay: 'always',
autohide: false,
}),
createCodeBlockExtension({
showBackground: true,
enableAutoDetection: true,
}),
// 键盘映射
keymap.of([
...closeBracketsKeymap,
]),
];
};

View File

@@ -1,111 +0,0 @@
import { EditorView } from '@codemirror/view';
import { Extension, Compartment } from '@codemirror/state';
// 字体配置接口
export interface FontConfig {
fontFamily: string;
fontSize?: number;
lineHeight?: number;
fontWeight?: string;
}
// 创建字体配置compartment
export const fontCompartment = new Compartment();
// 默认字体配置
export const DEFAULT_FONT_CONFIG: FontConfig = {
fontFamily: '"HarmonyOS Sans SC", "HarmonyOS Sans", "Microsoft YaHei", "PingFang SC", "Helvetica Neue", Arial, sans-serif',
fontSize: 14,
lineHeight: 1.5,
fontWeight: 'normal'
};
// 从后端配置创建字体配置
export function createFontConfigFromBackend(backendConfig: {
fontFamily?: string;
fontSize?: number;
lineHeight?: number;
fontWeight?: string;
}): FontConfig {
return {
fontFamily: backendConfig.fontFamily || DEFAULT_FONT_CONFIG.fontFamily,
fontSize: backendConfig.fontSize || DEFAULT_FONT_CONFIG.fontSize,
lineHeight: backendConfig.lineHeight || DEFAULT_FONT_CONFIG.lineHeight,
fontWeight: backendConfig.fontWeight || DEFAULT_FONT_CONFIG.fontWeight,
};
}
// 创建字体样式扩展
export function createFontExtension(config: Partial<FontConfig> = {}): Extension {
const fontConfig = { ...DEFAULT_FONT_CONFIG, ...config };
const styles: Record<string, any> = {
'&': {
fontFamily: fontConfig.fontFamily,
...(fontConfig.fontSize && { fontSize: `${fontConfig.fontSize}px` }),
...(fontConfig.lineHeight && { lineHeight: fontConfig.lineHeight.toString() }),
...(fontConfig.fontWeight && { fontWeight: fontConfig.fontWeight }),
},
'.cm-content': {
fontFamily: fontConfig.fontFamily,
...(fontConfig.fontSize && { fontSize: `${fontConfig.fontSize}px` }),
...(fontConfig.lineHeight && { lineHeight: fontConfig.lineHeight.toString() }),
...(fontConfig.fontWeight && { fontWeight: fontConfig.fontWeight }),
},
'.cm-editor': {
fontFamily: fontConfig.fontFamily,
},
'.cm-scroller': {
fontFamily: fontConfig.fontFamily,
},
'.cm-gutters': {
fontFamily: fontConfig.fontFamily,
...(fontConfig.fontSize && { fontSize: `${fontConfig.fontSize}px` }),
},
'.cm-lineNumbers': {
fontFamily: fontConfig.fontFamily,
...(fontConfig.fontSize && { fontSize: `${Math.max(10, fontConfig.fontSize - 1)}px` }),
},
'.cm-tooltip': {
fontFamily: fontConfig.fontFamily,
...(fontConfig.fontSize && { fontSize: `${Math.max(12, fontConfig.fontSize - 1)}px` }),
},
'.cm-completionLabel': {
fontFamily: fontConfig.fontFamily,
},
'.cm-completionDetail': {
fontFamily: fontConfig.fontFamily,
}
};
return EditorView.theme(styles);
}
// 创建响应式字体大小扩展
export function createResponsiveFontExtension(baseFontSize: number = 14): Extension {
return fontCompartment.of(createFontExtension({
fontSize: baseFontSize,
lineHeight: 1.5
}));
}
// 从后端配置创建字体扩展
export function createFontExtensionFromBackend(backendConfig: {
fontFamily?: string;
fontSize?: number;
lineHeight?: number;
fontWeight?: string;
}): Extension {
const fontConfig = createFontConfigFromBackend(backendConfig);
return fontCompartment.of(createFontExtension(fontConfig));
}
// 动态更新字体配置
export function updateFontConfig(view: EditorView, config: Partial<FontConfig>): void {
const newFontExtension = createFontExtension(config);
// 使用compartment重新配置字体扩展
view.dispatch({
effects: fontCompartment.reconfigure(newFontExtension)
});
}

View File

@@ -1,10 +0,0 @@
// 统一导出所有扩展
export * from './tabExtension';
export * from './wheelZoomExtension';
export * from './statsExtension';
export * from './autoSaveExtension';
export * from './fontExtension';
export * from './themeExtension';
export * from './codeblast';
export * from './codeblock';
export * from './keymap';

View File

@@ -1,298 +0,0 @@
import {KeyBindingCommand} from '@/../bindings/voidraft/internal/models/models'
import {
hideSearchVisibilityCommand,
searchReplaceAll,
searchShowReplace,
searchToggleCase,
searchToggleRegex,
searchToggleWholeWord,
showSearchVisibilityCommand
} from '../vscodeSearch/commands'
import {
addNewBlockAfterCurrent,
addNewBlockAfterLast,
addNewBlockBeforeCurrent,
deleteBlock,
formatCurrentBlock,
gotoNextBlock,
gotoPreviousBlock,
moveCurrentBlockDown,
moveCurrentBlockUp,
selectNextBlock,
selectPreviousBlock
} from '../codeblock/commands'
import { selectAll } from '../codeblock/selectAll'
import { deleteLineCommand } from '../codeblock/deleteLine'
import { moveLineUp, moveLineDown } from '../codeblock/moveLines'
import { transposeChars } from '@/views/editor/extensions'
import { copyCommand, cutCommand, pasteCommand } from '../codeblock/copyPaste'
import { undo, redo, undoSelection, redoSelection, cursorSyntaxLeft, cursorSyntaxRight, selectSyntaxLeft, selectSyntaxRight, copyLineUp, copyLineDown, insertBlankLine, selectLine, selectParentSyntax, indentLess, indentMore, indentSelection, cursorMatchingBracket, toggleComment, toggleBlockComment, insertNewlineAndIndent, deleteCharBackward, deleteCharForward, deleteGroupBackward, deleteGroupForward } from '@codemirror/commands'
import { foldCode, unfoldCode, foldAll, unfoldAll } from '@codemirror/language'
import i18n from '@/i18n'
// 默认编辑器选项
const defaultEditorOptions = {
defaultBlockToken: 'text',
defaultBlockAutoDetect: true,
}
/**
* 前端命令注册表
* 将后端定义的command字段映射到具体的前端方法和翻译键
*/
export const commandRegistry = {
[KeyBindingCommand.ShowSearchCommand]: {
handler: showSearchVisibilityCommand,
descriptionKey: 'keybindings.commands.showSearch'
},
[KeyBindingCommand.HideSearchCommand]: {
handler: hideSearchVisibilityCommand,
descriptionKey: 'keybindings.commands.hideSearch'
},
[KeyBindingCommand.SearchToggleCaseCommand]: {
handler: searchToggleCase,
descriptionKey: 'keybindings.commands.searchToggleCase'
},
[KeyBindingCommand.SearchToggleWordCommand]: {
handler: searchToggleWholeWord,
descriptionKey: 'keybindings.commands.searchToggleWord'
},
[KeyBindingCommand.SearchToggleRegexCommand]: {
handler: searchToggleRegex,
descriptionKey: 'keybindings.commands.searchToggleRegex'
},
[KeyBindingCommand.SearchShowReplaceCommand]: {
handler: searchShowReplace,
descriptionKey: 'keybindings.commands.searchShowReplace'
},
[KeyBindingCommand.SearchReplaceAllCommand]: {
handler: searchReplaceAll,
descriptionKey: 'keybindings.commands.searchReplaceAll'
},
// 代码块操作命令
[KeyBindingCommand.BlockSelectAllCommand]: {
handler: selectAll,
descriptionKey: 'keybindings.commands.blockSelectAll'
},
[KeyBindingCommand.BlockAddAfterCurrentCommand]: {
handler: addNewBlockAfterCurrent(defaultEditorOptions),
descriptionKey: 'keybindings.commands.blockAddAfterCurrent'
},
[KeyBindingCommand.BlockAddAfterLastCommand]: {
handler: addNewBlockAfterLast(defaultEditorOptions),
descriptionKey: 'keybindings.commands.blockAddAfterLast'
},
[KeyBindingCommand.BlockAddBeforeCurrentCommand]: {
handler: addNewBlockBeforeCurrent(defaultEditorOptions),
descriptionKey: 'keybindings.commands.blockAddBeforeCurrent'
},
[KeyBindingCommand.BlockGotoPreviousCommand]: {
handler: gotoPreviousBlock,
descriptionKey: 'keybindings.commands.blockGotoPrevious'
},
[KeyBindingCommand.BlockGotoNextCommand]: {
handler: gotoNextBlock,
descriptionKey: 'keybindings.commands.blockGotoNext'
},
[KeyBindingCommand.BlockSelectPreviousCommand]: {
handler: selectPreviousBlock,
descriptionKey: 'keybindings.commands.blockSelectPrevious'
},
[KeyBindingCommand.BlockSelectNextCommand]: {
handler: selectNextBlock,
descriptionKey: 'keybindings.commands.blockSelectNext'
},
[KeyBindingCommand.BlockDeleteCommand]: {
handler: deleteBlock(defaultEditorOptions),
descriptionKey: 'keybindings.commands.blockDelete'
},
[KeyBindingCommand.BlockMoveUpCommand]: {
handler: moveCurrentBlockUp,
descriptionKey: 'keybindings.commands.blockMoveUp'
},
[KeyBindingCommand.BlockMoveDownCommand]: {
handler: moveCurrentBlockDown,
descriptionKey: 'keybindings.commands.blockMoveDown'
},
[KeyBindingCommand.BlockDeleteLineCommand]: {
handler: deleteLineCommand,
descriptionKey: 'keybindings.commands.blockDeleteLine'
},
[KeyBindingCommand.BlockMoveLineUpCommand]: {
handler: moveLineUp,
descriptionKey: 'keybindings.commands.blockMoveLineUp'
},
[KeyBindingCommand.BlockMoveLineDownCommand]: {
handler: moveLineDown,
descriptionKey: 'keybindings.commands.blockMoveLineDown'
},
[KeyBindingCommand.BlockTransposeCharsCommand]: {
handler: transposeChars,
descriptionKey: 'keybindings.commands.blockTransposeChars'
},
[KeyBindingCommand.BlockFormatCommand]: {
handler: formatCurrentBlock,
descriptionKey: 'keybindings.commands.blockFormat'
},
[KeyBindingCommand.BlockCopyCommand]: {
handler: copyCommand,
descriptionKey: 'keybindings.commands.blockCopy'
},
[KeyBindingCommand.BlockCutCommand]: {
handler: cutCommand,
descriptionKey: 'keybindings.commands.blockCut'
},
[KeyBindingCommand.BlockPasteCommand]: {
handler: pasteCommand,
descriptionKey: 'keybindings.commands.blockPaste'
},
[KeyBindingCommand.HistoryUndoCommand]: {
handler: undo,
descriptionKey: 'keybindings.commands.historyUndo'
},
[KeyBindingCommand.HistoryRedoCommand]: {
handler: redo,
descriptionKey: 'keybindings.commands.historyRedo'
},
[KeyBindingCommand.HistoryUndoSelectionCommand]: {
handler: undoSelection,
descriptionKey: 'keybindings.commands.historyUndoSelection'
},
[KeyBindingCommand.HistoryRedoSelectionCommand]: {
handler: redoSelection,
descriptionKey: 'keybindings.commands.historyRedoSelection'
},
[KeyBindingCommand.FoldCodeCommand]: {
handler: foldCode,
descriptionKey: 'keybindings.commands.foldCode'
},
[KeyBindingCommand.UnfoldCodeCommand]: {
handler: unfoldCode,
descriptionKey: 'keybindings.commands.unfoldCode'
},
[KeyBindingCommand.FoldAllCommand]: {
handler: foldAll,
descriptionKey: 'keybindings.commands.foldAll'
},
[KeyBindingCommand.UnfoldAllCommand]: {
handler: unfoldAll,
descriptionKey: 'keybindings.commands.unfoldAll'
},
[KeyBindingCommand.CursorSyntaxLeftCommand]: {
handler: cursorSyntaxLeft,
descriptionKey: 'keybindings.commands.cursorSyntaxLeft'
},
[KeyBindingCommand.CursorSyntaxRightCommand]: {
handler: cursorSyntaxRight,
descriptionKey: 'keybindings.commands.cursorSyntaxRight'
},
[KeyBindingCommand.SelectSyntaxLeftCommand]: {
handler: selectSyntaxLeft,
descriptionKey: 'keybindings.commands.selectSyntaxLeft'
},
[KeyBindingCommand.SelectSyntaxRightCommand]: {
handler: selectSyntaxRight,
descriptionKey: 'keybindings.commands.selectSyntaxRight'
},
[KeyBindingCommand.CopyLineUpCommand]: {
handler: copyLineUp,
descriptionKey: 'keybindings.commands.copyLineUp'
},
[KeyBindingCommand.CopyLineDownCommand]: {
handler: copyLineDown,
descriptionKey: 'keybindings.commands.copyLineDown'
},
[KeyBindingCommand.InsertBlankLineCommand]: {
handler: insertBlankLine,
descriptionKey: 'keybindings.commands.insertBlankLine'
},
[KeyBindingCommand.SelectLineCommand]: {
handler: selectLine,
descriptionKey: 'keybindings.commands.selectLine'
},
[KeyBindingCommand.SelectParentSyntaxCommand]: {
handler: selectParentSyntax,
descriptionKey: 'keybindings.commands.selectParentSyntax'
},
[KeyBindingCommand.IndentLessCommand]: {
handler: indentLess,
descriptionKey: 'keybindings.commands.indentLess'
},
[KeyBindingCommand.IndentMoreCommand]: {
handler: indentMore,
descriptionKey: 'keybindings.commands.indentMore'
},
[KeyBindingCommand.IndentSelectionCommand]: {
handler: indentSelection,
descriptionKey: 'keybindings.commands.indentSelection'
},
[KeyBindingCommand.CursorMatchingBracketCommand]: {
handler: cursorMatchingBracket,
descriptionKey: 'keybindings.commands.cursorMatchingBracket'
},
[KeyBindingCommand.ToggleCommentCommand]: {
handler: toggleComment,
descriptionKey: 'keybindings.commands.toggleComment'
},
[KeyBindingCommand.ToggleBlockCommentCommand]: {
handler: toggleBlockComment,
descriptionKey: 'keybindings.commands.toggleBlockComment'
},
[KeyBindingCommand.InsertNewlineAndIndentCommand]: {
handler: insertNewlineAndIndent,
descriptionKey: 'keybindings.commands.insertNewlineAndIndent'
},
[KeyBindingCommand.DeleteCharBackwardCommand]: {
handler: deleteCharBackward,
descriptionKey: 'keybindings.commands.deleteCharBackward'
},
[KeyBindingCommand.DeleteCharForwardCommand]: {
handler: deleteCharForward,
descriptionKey: 'keybindings.commands.deleteCharForward'
},
[KeyBindingCommand.DeleteGroupBackwardCommand]: {
handler: deleteGroupBackward,
descriptionKey: 'keybindings.commands.deleteGroupBackward'
},
[KeyBindingCommand.DeleteGroupForwardCommand]: {
handler: deleteGroupForward,
descriptionKey: 'keybindings.commands.deleteGroupForward'
},
} as const
/**
* 获取命令处理函数
* @param command 命令名称
* @returns 对应的处理函数,如果不存在则返回 undefined
*/
export const getCommandHandler = (command: KeyBindingCommand) => {
return commandRegistry[command]?.handler
}
/**
* 获取命令描述
* @param command 命令名称
* @returns 对应的描述,如果不存在则返回 undefined
*/
export const getCommandDescription = (command: KeyBindingCommand) => {
const descriptionKey = commandRegistry[command]?.descriptionKey
return descriptionKey ? i18n.global.t(descriptionKey) : undefined
}
/**
* 检查命令是否已注册
* @param command 命令名称
* @returns 是否已注册
*/
export const isCommandRegistered = (command: KeyBindingCommand): boolean => {
return command in commandRegistry
}
/**
* 获取所有已注册的命令
* @returns 已注册的命令列表
*/
export const getRegisteredCommands = (): KeyBindingCommand[] => {
return Object.keys(commandRegistry) as KeyBindingCommand[]
}

View File

@@ -1,23 +0,0 @@
import { Extension } from '@codemirror/state'
import { useKeybindingStore } from '@/stores/keybindingStore'
import { KeymapManager } from './keymapManager'
/**
* 异步创建快捷键扩展
* 确保快捷键配置已加载
*/
export const createDynamicKeymapExtension = async (): Promise<Extension> => {
const keybindingStore = useKeybindingStore()
// 确保快捷键配置已加载
if (keybindingStore.keyBindings.length === 0) {
await keybindingStore.loadKeyBindings()
}
return KeymapManager.createKeymapExtension(keybindingStore.enabledKeyBindings)
}
// 导出相关模块
export { KeymapManager } from './keymapManager'
export { commandRegistry, getCommandHandler, getCommandDescription, isCommandRegistered, getRegisteredCommands } from './commandRegistry'
export type { KeyBinding, CommandHandler, CommandDefinition, KeymapResult } from './types'

View File

@@ -1,84 +0,0 @@
import {keymap} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {KeyBinding as KeyBindingConfig} from '@/../bindings/voidraft/internal/models/models'
import {KeyBinding, KeymapResult} from './types'
import {getCommandHandler, isCommandRegistered} from './commandRegistry'
/**
* 快捷键管理器
* 负责将后端配置转换为CodeMirror快捷键扩展
*/
export class KeymapManager {
/**
* 将后端快捷键配置转换为CodeMirror快捷键绑定
* @param keyBindings 后端快捷键配置列表
* @returns 转换结果
*/
static convertToKeyBindings(keyBindings: KeyBindingConfig[]): KeymapResult {
const result: KeyBinding[] = []
for (const binding of keyBindings) {
// 跳过禁用的快捷键
if (!binding.enabled) {
continue
}
// 检查命令是否已注册
if (!isCommandRegistered(binding.command)) {
continue
}
// 获取命令处理函数
const handler = getCommandHandler(binding.command)
if (!handler) {
continue
}
// 转换为CodeMirror快捷键格式
const keyBinding: KeyBinding = {
key: binding.key,
run: handler,
preventDefault: true
}
result.push(keyBinding)
}
return {keyBindings: result}
}
/**
* 创建CodeMirror快捷键扩展
* @param keyBindings 后端快捷键配置列表
* @returns CodeMirror扩展
*/
static createKeymapExtension(keyBindings: KeyBindingConfig[]): Extension {
const {keyBindings: cmKeyBindings} =
this.convertToKeyBindings(keyBindings)
return keymap.of(cmKeyBindings)
}
/**
* 验证快捷键配置
* @param keyBindings 快捷键配置列表
* @returns 验证结果
*/
static validateKeyBindings(keyBindings: KeyBindingConfig[]): {
valid: KeyBindingConfig[]
invalid: KeyBindingConfig[]
} {
const valid: KeyBindingConfig[] = []
const invalid: KeyBindingConfig[] = []
for (const binding of keyBindings) {
if (binding.enabled && binding.key && isCommandRegistered(binding.command)) {
valid.push(binding)
} else {
invalid.push(binding)
}
}
return {valid, invalid}
}
}

View File

@@ -1,30 +0,0 @@
import {Command} from '@codemirror/view'
/**
* CodeMirror快捷键绑定格式
*/
export interface KeyBinding {
key: string
run: Command
preventDefault?: boolean
}
/**
* 命令处理函数类型
*/
export type CommandHandler = Command
/**
* 命令定义接口
*/
export interface CommandDefinition {
handler: CommandHandler
descriptionKey: string // 翻译键
}
/**
* 快捷键转换结果
*/
export interface KeymapResult {
keyBindings: KeyBinding[]
}

View File

@@ -70,7 +70,7 @@ const rainbowBracketsPlugin = ViewPlugin.fromClass(RainbowBracketsView, {
decorations: (v) => v.decorations,
});
export default function rainbowBrackets() {
export default function rainbowBracketsExtension() {
return [
rainbowBracketsPlugin,
EditorView.baseTheme({

View File

@@ -1,65 +0,0 @@
import {Extension} from '@codemirror/state';
import {EditorView} from '@codemirror/view';
import {DocumentStats} from '@/stores/editorStore';
import {getActiveNoteBlock} from '@/views/editor/extensions/codeblock/state';
// 更新编辑器文档统计信息
export const updateStats = (
view: EditorView,
updateDocumentStats: (stats: DocumentStats) => void
) => {
if (!view) return;
const state = view.state;
// 获取当前光标所在的代码块
const activeBlock = getActiveNoteBlock(state as any);
if (!activeBlock) {
// 如果没有活动块,显示空统计
updateDocumentStats({
lines: 0,
characters: 0,
selectedCharacters: 0
});
return;
}
// 获取当前块的内容范围
const blockContent = state.doc.sliceString(activeBlock.content.from, activeBlock.content.to);
// 计算块内容的行数
const blockLines = blockContent.split('\n').length;
// 计算选中的字符数(只统计在当前块内的选中内容)
let selectedChars = 0;
const selections = state.selection;
if (selections) {
for (let i = 0; i < selections.ranges.length; i++) {
const range = selections.ranges[i];
// 计算选中范围与当前块内容范围的交集
const selectionStart = Math.max(range.from, activeBlock.content.from);
const selectionEnd = Math.min(range.to, activeBlock.content.to);
if (selectionStart < selectionEnd) {
selectedChars += selectionEnd - selectionStart;
}
}
}
updateDocumentStats({
lines: blockLines,
characters: blockContent.length,
selectedCharacters: selectedChars
});
};
// 创建统计信息更新监听器扩展
export const createStatsUpdateExtension = (
updateDocumentStats: (stats: DocumentStats) => void
): Extension => {
return EditorView.updateListener.of(update => {
if (update.docChanged || update.selectionSet) {
updateStats(update.view, updateDocumentStats);
}
});
};

View File

@@ -1,78 +0,0 @@
import {Compartment, Extension} from '@codemirror/state';
import {EditorView, keymap} from '@codemirror/view';
import {indentSelection} from '@codemirror/commands';
import {indentUnit} from '@codemirror/language';
import {TabType} from '@/../bindings/voidraft/internal/models/models';
// Tab设置相关的compartment
export const tabSizeCompartment = new Compartment();
export const tabKeyCompartment = new Compartment();
// 自定义Tab键处理函数
export const tabHandler = (view: EditorView, tabSize: number, tabType: TabType): boolean => {
// 如果有选中文本使用indentSelection
if (!view.state.selection.main.empty) {
return indentSelection(view);
}
// 根据tabType创建缩进字符
const indent = tabType === 'spaces' ? ' '.repeat(tabSize) : '\t';
// 在光标位置插入缩进字符
const {state, dispatch} = view;
dispatch(state.update(state.replaceSelection(indent), {scrollIntoView: true}));
return true;
};
// 获取Tab相关的扩展
export const getTabExtensions = (tabSize: number, enableTabIndent: boolean, tabType: TabType): Extension[] => {
const extensions: Extension[] = [];
// 根据tabType设置缩进单位
const indentStr = tabType === 'spaces' ? ' '.repeat(tabSize) : '\t';
extensions.push(tabSizeCompartment.of(indentUnit.of(indentStr)));
// 如果启用了Tab缩进添加自定义Tab键映射
if (enableTabIndent) {
extensions.push(
tabKeyCompartment.of(
keymap.of([{
key: "Tab",
run: (view) => tabHandler(view, tabSize, tabType)
}])
)
);
} else {
extensions.push(tabKeyCompartment.of([]));
}
return extensions;
};
// 更新Tab配置
export const updateTabConfig = (
view: EditorView | null,
tabSize: number,
enableTabIndent: boolean,
tabType: TabType
) => {
if (!view) return;
// 根据tabType更新indentUnit配置
const indentStr = tabType === 'spaces' ? ' '.repeat(tabSize) : '\t';
view.dispatch({
effects: tabSizeCompartment.reconfigure(indentUnit.of(indentStr))
});
// 更新Tab键映射
const tabKeymap = enableTabIndent
? keymap.of([{
key: "Tab",
run: (view) => tabHandler(view, tabSize, tabType)
}])
: [];
view.dispatch({
effects: tabKeyCompartment.reconfigure(tabKeymap)
});
};

View File

@@ -1,53 +0,0 @@
import { Extension, Compartment } from '@codemirror/state';
import { EditorView } from '@codemirror/view';
import { SystemThemeType } from '@/../bindings/voidraft/internal/models/models';
import { dark } from '@/views/editor/theme/dark';
import { light } from '@/views/editor/theme/light';
// 主题区间 - 用于动态切换主题
export const themeCompartment = new Compartment();
/**
* 根据主题类型获取主题扩展
*/
const getThemeExtension = (themeType: SystemThemeType): Extension => {
// 处理 auto 主题类型
let actualTheme: SystemThemeType = themeType;
if (themeType === SystemThemeType.SystemThemeAuto) {
actualTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
? SystemThemeType.SystemThemeDark
: SystemThemeType.SystemThemeLight;
}
// 直接返回对应的主题扩展
switch (actualTheme) {
case SystemThemeType.SystemThemeLight:
return light;
case SystemThemeType.SystemThemeDark:
default:
return dark;
}
};
/**
* 创建主题扩展(用于编辑器初始化)
*/
export const createThemeExtension = (themeType: SystemThemeType = SystemThemeType.SystemThemeDark): Extension => {
const extension = getThemeExtension(themeType);
return themeCompartment.of(extension);
};
/**
* 更新编辑器主题
*/
export const updateEditorTheme = (view: EditorView, themeType: SystemThemeType): void => {
if (!view?.dispatch) {
return;
}
const extension = getThemeExtension(themeType);
view.dispatch({
effects: themeCompartment.reconfigure(extension)
});
};

View File

@@ -1,22 +0,0 @@
// 处理滚轮缩放字体的事件处理函数
export const createWheelZoomHandler = (
increaseFontSize: () => void,
decreaseFontSize: () => void
) => {
return (event: WheelEvent) => {
// 检查是否按住了Ctrl键
if (event.ctrlKey) {
// 阻止默认行为(防止页面缩放)
event.preventDefault();
// 根据滚轮方向增大或减小字体
if (event.deltaY < 0) {
// 向上滚动,增大字体
increaseFontSize();
} else {
// 向下滚动,减小字体
decreaseFontSize();
}
}
};
};