Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions web/src/components/MemoEditor/Editor/CommandSuggestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@ const CommandSuggestions = observer(({ editorRef, editorActions, commands }: Com
onAutocomplete: (cmd, word, index, actions) => {
// Replace the trigger word with the command output
actions.removeText(index, word.length);
const initialPosition = actions.getCursorPosition();
actions.insertText(cmd.run());
// Position cursor if command specifies an offset
if (cmd.cursorOffset) {
actions.setCursorPosition(actions.getCursorPosition() + cmd.cursorOffset);
}

const offset = cmd.cursorRange?.[0];
if (typeof offset === "undefined") return;

const cursorPosition = initialPosition + offset;
const length = cmd.cursorRange?.[1] || 0;
actions.setCursorPosition(cursorPosition, cursorPosition + length);
},
});

Expand Down
5 changes: 4 additions & 1 deletion web/src/components/MemoEditor/Editor/SuggestionsPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export function SuggestionsPopup<T>({
<div
key={getItemKey(item, i)}
ref={i === selectedIndex ? selectedItemRef : null}
onMouseDown={() => onItemSelect(item)}
onMouseDown={(e) => {
onItemSelect(item);
e.preventDefault();
}}
className={cn(
"rounded p-1 px-2 w-full text-sm cursor-pointer transition-colors select-none",
"hover:bg-accent hover:text-accent-foreground",
Expand Down
41 changes: 30 additions & 11 deletions web/src/components/MemoEditor/Editor/commands.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,47 @@
export interface Command {
name: string;
run: () => string;
cursorOffset?: number;
// Ex.:
// [4] == [4, 0] - cursor offset is 4 chars from initial position
// [7, 2] - cursor offset is 7 chars and 2 next chars selected
// If omitted, cursor stays in the end of the inserted string
cursorRange?: number[];
}

export const editorCommands: Command[] = [
{
name: "todo",
run: () => "- [ ] ",
cursorOffset: 6, // Places cursor after "- [ ] " to start typing task
name: "code",
run: () => "```js\n\n```", // JS by default as most popular (at least on github)
cursorRange: [3, 2],
},
{
name: "code",
run: () => "```\n\n```",
cursorOffset: 4, // Places cursor on empty line between code fences
// Template from github, but with summary initially selected for better UX
name: "details",
run: () => "<details><summary>Details</summary>\n\n\n</details>",
cursorRange: [18, 7],
},
{
name: "image",
run: () => "![alt text]()", // No need in URL placeholder
cursorRange: [2, 8],
},
{
name: "link",
run: () => "[text](url)",
cursorOffset: 1, // Places cursor after "[" to type link text
run: () => "[text]()",
cursorRange: [1, 4],
},
{
name: "table",
run: () => "| Header | Header |\n| ------ | ------ |\n| Cell | Cell |",
cursorOffset: 1, // Places cursor after first "|" to edit first header
run: () => "| Column1 | Column2 |\n| ------ | ------ |\n| Cell1 | Cell2 |",
cursorRange: [2, 7],
},
{
name: "todo",
run: () => "- [ ] ",
},
{
name: "youtube",
run: () => "[![alt text](https://img.youtube.com/vi/VIDEO_ID/0.jpg)](https://www.youtube.com/watch?v=VIDEO_ID)",
cursorRange: [3, 8],
},
];