diff --git a/web/src/components/MemoEditor/Editor/index.tsx b/web/src/components/MemoEditor/Editor/index.tsx index d64d1881f..3f7d724e9 100644 --- a/web/src/components/MemoEditor/Editor/index.tsx +++ b/web/src/components/MemoEditor/Editor/index.tsx @@ -1,10 +1,10 @@ import { forwardRef, ReactNode, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react"; import { cn } from "@/lib/utils"; -import { detectLastListItem, generateListContinuation } from "@/utils/markdown-list-detection"; import { Command } from "../types/command"; import CommandSuggestions from "./CommandSuggestions"; import TagSuggestions from "./TagSuggestions"; import { editorCommands } from "./commands"; +import { useListAutoCompletion } from "./useListAutoCompletion"; export interface EditorRefActions { getEditor: () => HTMLTextAreaElement | null; @@ -152,22 +152,14 @@ const Editor = forwardRef(function Editor(props: Props, ref: React.ForwardedRef< updateEditorHeight(); }, []); - const handleEditorKeyDown = async (event: React.KeyboardEvent) => { - if (event.key === "Enter" && !isInIME) { - if (event.shiftKey || event.ctrlKey || event.metaKey || event.altKey) { - return; - } + const { handleEnterKey } = useListAutoCompletion({ + editorActions, + isInIME, + }); - const cursorPosition = editorActions.getCursorPosition(); - const prevContent = editorActions.getContent().substring(0, cursorPosition); - - // Detect list item using regex-based detection - const listInfo = detectLastListItem(prevContent); - if (listInfo.type) { - event.preventDefault(); - const insertText = "\n" + generateListContinuation(listInfo); - editorActions.insertText(insertText); - } + const handleEditorKeyDown = (event: React.KeyboardEvent) => { + if (event.key === "Enter") { + handleEnterKey(event); } }; diff --git a/web/src/components/MemoEditor/Editor/useListAutoCompletion.ts b/web/src/components/MemoEditor/Editor/useListAutoCompletion.ts new file mode 100644 index 000000000..eddc326db --- /dev/null +++ b/web/src/components/MemoEditor/Editor/useListAutoCompletion.ts @@ -0,0 +1,57 @@ +import { useCallback } from "react"; +import { detectLastListItem, generateListContinuation } from "@/utils/markdown-list-detection"; +import { EditorRefActions } from "."; + +interface UseListAutoCompletionOptions { + editorActions: EditorRefActions; + isInIME: boolean; +} + +/** + * Custom hook for handling markdown list auto-completion. + * When the user presses Enter on a list item, this hook automatically + * continues the list with the appropriate formatting. + * + * Supports: + * - Ordered lists (1. item, 2. item, etc.) + * - Unordered lists (- item, * item, + item) + * - Task lists (- [ ] task, - [x] task) + * - Nested lists with proper indentation + */ +export function useListAutoCompletion({ editorActions, isInIME }: UseListAutoCompletionOptions) { + /** + * Handles the Enter key press to auto-complete list items. + * Returns true if the event was handled, false otherwise. + */ + const handleEnterKey = useCallback( + (event: React.KeyboardEvent): boolean => { + // Don't handle if in IME composition (for Asian languages) + if (isInIME) { + return false; + } + + // Don't handle if modifier keys are pressed (user wants manual control) + if (event.shiftKey || event.ctrlKey || event.metaKey || event.altKey) { + return false; + } + + const cursorPosition = editorActions.getCursorPosition(); + const contentBeforeCursor = editorActions.getContent().substring(0, cursorPosition); + + // Detect if we're on a list item + const listInfo = detectLastListItem(contentBeforeCursor); + + if (listInfo.type) { + event.preventDefault(); + const continuation = generateListContinuation(listInfo); + editorActions.insertText("\n" + continuation); + return true; + } + + return false; + }, + [editorActions, isInIME], + ); + + return { handleEnterKey }; +}