chore(web): simplify command system and improve editor UX (#5242)

Streamlines the command suggestion interface and fixes list auto-completion behavior:

- Remove command descriptions for cleaner suggestion popup UI
- Replace PaperclipIcon with FileIcon for semantic accuracy
- Fix list auto-completion to avoid extra newline when exiting list mode
- Add explanatory comments for cursor offset positions
- Improve dependency array in useListAutoCompletion hook

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
pull/5242/head
Johnny 6 days ago
parent fc43f86571
commit 5925e3cfc1

@ -1,6 +1,6 @@
import { LatLng } from "leaflet"; import { LatLng } from "leaflet";
import { uniqBy } from "lodash-es"; import { uniqBy } from "lodash-es";
import { LinkIcon, LoaderIcon, MapPinIcon, PaperclipIcon, PlusIcon } from "lucide-react"; import { FileIcon, LinkIcon, LoaderIcon, MapPinIcon, PlusIcon } from "lucide-react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -113,7 +113,7 @@ const InsertMenu = observer((props: Props) => {
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start"> <DropdownMenuContent align="start">
<DropdownMenuItem onClick={handleUploadClick}> <DropdownMenuItem onClick={handleUploadClick}>
<PaperclipIcon className="w-4 h-4" /> <FileIcon className="w-4 h-4" />
{t("common.upload")} {t("common.upload")}
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={() => setLinkDialogOpen(true)}> <DropdownMenuItem onClick={() => setLinkDialogOpen(true)}>

@ -51,12 +51,7 @@ const CommandSuggestions = observer(({ editorRef, editorActions, commands }: Com
selectedIndex={selectedIndex} selectedIndex={selectedIndex}
onItemSelect={handleItemSelect} onItemSelect={handleItemSelect}
getItemKey={(cmd) => cmd.name} getItemKey={(cmd) => cmd.name}
renderItem={(cmd) => ( renderItem={(cmd) => <OverflowTip>/{cmd.name}</OverflowTip>}
<>
<OverflowTip>/{cmd.name}</OverflowTip>
{cmd.description && <span className="ml-2 text-xs text-muted-foreground">{cmd.description}</span>}
</>
)}
/> />
); );
}); });

@ -3,32 +3,27 @@ import { Command } from "@/components/MemoEditor/types/command";
export const editorCommands: Command[] = [ export const editorCommands: Command[] = [
{ {
name: "todo", name: "todo",
description: "Insert a task checkbox",
run: () => "- [ ] ", run: () => "- [ ] ",
cursorOffset: 6, cursorOffset: 6, // Places cursor after "- [ ] " to start typing task
}, },
{ {
name: "code", name: "code",
description: "Insert a code block",
run: () => "```\n\n```", run: () => "```\n\n```",
cursorOffset: 4, cursorOffset: 4, // Places cursor on empty line between code fences
}, },
{ {
name: "link", name: "link",
description: "Insert a link",
run: () => "[text](url)", run: () => "[text](url)",
cursorOffset: 1, cursorOffset: 1, // Places cursor after "[" to type link text
}, },
{ {
name: "table", name: "table",
description: "Insert a table",
run: () => "| Header | Header |\n| ------ | ------ |\n| Cell | Cell |", run: () => "| Header | Header |\n| ------ | ------ |\n| Cell | Cell |",
cursorOffset: 1, cursorOffset: 1, // Places cursor after first "|" to edit first header
}, },
{ {
name: "highlight", name: "highlight",
description: "Insert highlighted text",
run: () => "==text==", run: () => "==text==",
cursorOffset: 2, cursorOffset: 2, // Places cursor between "==" markers to type highlighted text
}, },
]; ];

@ -68,7 +68,6 @@ export function useListAutoCompletion({ editorRef, editorActions, isInIME }: Use
// Remove the empty list marker and exit list mode // Remove the empty list marker and exit list mode
const lineStartPos = cursorPosition - currentLine.length; const lineStartPos = cursorPosition - currentLine.length;
actions.removeText(lineStartPos, currentLine.length); actions.removeText(lineStartPos, currentLine.length);
actions.insertText("\n");
} else { } else {
// Continue the list with the next item // Continue the list with the next item
const continuation = generateListContinuation(listInfo); const continuation = generateListContinuation(listInfo);
@ -82,5 +81,5 @@ export function useListAutoCompletion({ editorRef, editorActions, isInIME }: Use
return () => { return () => {
editor.removeEventListener("keydown", handleKeyDown); editor.removeEventListener("keydown", handleKeyDown);
}; };
}, [editorRef.current]); }, []); // Editor ref is stable; state accessed via refs to avoid stale closures
} }

@ -1,6 +1,5 @@
export type Command = { export type Command = {
name: string; name: string;
description?: string;
run: () => string; run: () => string;
cursorOffset?: number; cursorOffset?: number;
}; };

Loading…
Cancel
Save