mirror of https://github.com/usememos/memos.git
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>
This commit is contained in:
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…
Reference in New Issue