import { type Component, createSignal, For, createEffect } from "solid-js"; import { Search, Save, FolderOpen, Tag, Settings, Database, Server, RefreshCw, Cpu, ChevronRight, X, Calendar, Pen, Type, Underline, Strikethrough, Code, Quote, ImageIcon, Heading1, Heading2, Heading3, Link, CheckSquare, List, ListOrdered, Film, AppWindow, Minus, Grid3X3, AlignLeft, AlignCenter, AlignRight, FileText, Delete, Trash2, Highlighter, Bold, Italic, ListTodo } from "lucide-solid"; import { cn } from "@/lib/utils"; export interface CommandItem { title: string; description: string; aliases?: string[]; icon: any; command: (props: { editor: any; range: any }) => void; } export const getSuggestionItems = ({ query }: { query: string }): CommandItem[] => { return [ { title: "Text", description: "Just start typing with plain text.", icon: Type, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).setNode("paragraph").run(); }, }, { title: "Bold", description: "Strong text. (**text**)", aliases: ["b", "strong"], icon: Bold, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).toggleBold().run(); }, }, { title: "Italic", description: "Emphasized text. (*text*)", aliases: ["i", "emphasis"], icon: Italic, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).toggleItalic().run(); }, }, { title: "Underline", description: "Underlined text.", aliases: ["u"], icon: Underline, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).toggleUnderline().run(); }, }, { title: "Heading 1", description: "Big section heading.", aliases: ["h1"], icon: Heading1, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run(); }, }, { title: "Heading 2", description: "Medium section heading.", aliases: ["h2"], icon: Heading2, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).setNode("heading", { level: 2 }).run(); }, }, { title: "Heading 3", description: "Small section heading.", aliases: ["h3"], icon: Heading3, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).setNode("heading", { level: 3 }).run(); }, }, { title: "Bullet List", description: "Create a simple bulleted list.", icon: List, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).toggleBulletList().run(); }, }, { title: "Checklist", description: "Insert a checklist.", icon: ListTodo, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).toggleTaskList().run(); }, }, { title: "Code Block", description: "Capture a code snippet.", icon: Code, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).toggleCodeBlock().run(); }, }, { title: "Blockquote", description: "Capture a quote.", icon: Quote, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).toggleBlockquote().run(); }, }, { title: "Image", description: "Upload an image.", icon: ImageIcon, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).uploadImage().run(); }, }, { title: "Video", description: "Upload a video.", aliases: ["movie", "mp4", "mov"], icon: Film, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).uploadVideo().run(); }, }, { title: "File", description: "Upload a document or file.", aliases: ["file", "pdf", "doc", "upload"], icon: FileText, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).uploadFile().run(); }, }, { title: "Job File", description: "Embed a job file from Prism.", aliases: ["job", "jobfile", "prism"], icon: AppWindow, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).insertFileViewer().run(); }, }, { title: "Divider", description: "Insert a horizontal rule.", icon: Minus, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).setHorizontalRule().run(); }, }, { title: "Table", description: "Insert a 3x3 table.", aliases: ["grid", "table"], icon: Grid3X3, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(); }, }, { title: "Highlight", description: "Highlight selected text.", aliases: ["mark"], icon: Highlighter, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).toggleHighlight().run(); }, }, { title: "Align Left", description: "Align text to the left.", icon: AlignLeft, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).setTextAlign("left").run(); }, }, { title: "Align Center", description: "Align text to the center.", icon: AlignCenter, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).setTextAlign("center").run(); }, }, { title: "Align Right", description: "Align text to the right.", icon: AlignRight, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).setTextAlign("right").run(); }, }, { title: "Add Row Above", description: "Insert a new row above the cursor.", icon: Grid3X3, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).addRowBefore().run(); }, }, { title: "Add Row Below", description: "Insert a new row below the cursor.", icon: Grid3X3, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).addRowAfter().run(); }, }, { title: "Delete Row", description: "Delete the current row.", icon: Trash2, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).deleteRow().run(); }, }, { title: "Add Column Left", description: "Insert a new column to the left.", icon: Grid3X3, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).addColumnBefore().run(); }, }, { title: "Add Column Right", description: "Insert a new column to the right.", icon: Grid3X3, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).addColumnAfter().run(); }, }, { title: "Delete Column", description: "Delete the current column.", icon: Trash2, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).deleteColumn().run(); }, }, { title: "Delete Table", description: "Delete the entire table.", icon: Trash2, command: ({ editor, range }: { editor: any, range: any }) => { editor.chain().focus().deleteRange(range).deleteTable().run(); }, }, ].filter(item => { const lowerQuery = query.toLowerCase(); return item.title.toLowerCase().startsWith(lowerQuery) || item.aliases?.some(alias => alias.toLowerCase().startsWith(lowerQuery)); }); }; export const SlashMenu: Component<{ items: CommandItem[]; command: (item: CommandItem) => void; editor: any; ref: (handlers: { onKeyDown: (e: KeyboardEvent) => boolean }) => void; }> = (props) => { const [selectedIndex, setSelectedIndex] = createSignal(0); // Auto-select the first item when the list changes (filtering) createEffect(() => { // We track props.items. When it changes, we reset. props.items; setSelectedIndex(0); }); const selectItem = (index: number) => { const item = props.items[index]; if (item) { props.command(item); } }; // Expose handlers to the parent via the ref prop props.ref({ onKeyDown: (e: KeyboardEvent) => { if (e.key === "ArrowUp") { setSelectedIndex((prev) => (prev + props.items.length - 1) % props.items.length); return true; } if (e.key === "ArrowDown") { setSelectedIndex((prev) => (prev + 1) % props.items.length); return true; } if (e.key === "Enter") { selectItem(selectedIndex()); return true; } return false; } }); return (