mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-21 18:13:26 +00:00
fix(editor): match slash menu against id + keywords (BUG-1419) (#551)
Typing `/h2` (or `/ul`, `/hr`, `/todo`, etc.) in the tiptap editor
auto-closed the slash menu because the filter only matched on `label`
and `description`. "Heading 2".includes("h2") is false — the space
between "Heading" and "2" breaks the substring match — and zero
matches triggers closeSlash(), so the picker vanished as soon as the
user typed the second character.
Add optional `keywords?: string[]` to BlockType and populate common
abbreviations per block (h1/h2/h3, ul/ol, todo/checkbox, hr/rule,
quote/bq, code, html, tbl, etc.). Extend getFilteredSlash() to join
label + description + id + keywords into a single lowercased haystack
and substring-match the query against it.
Pure UI filter change — Y.Doc / ProseMirror shape unchanged, no
SCHEMA_VERSION bump. Turn-into menu unaffected (no filter there).
This commit is contained in:
@@ -425,7 +425,12 @@
|
||||
function getFilteredSlash() {
|
||||
if (!slashQuery) return SLASH_ITEMS;
|
||||
const q = slashQuery.toLowerCase();
|
||||
return SLASH_ITEMS.filter(i => i.label.toLowerCase().includes(q) || i.description.toLowerCase().includes(q));
|
||||
return SLASH_ITEMS.filter((i) => {
|
||||
const hay = [i.label, i.description, i.id, ...(i.keywords ?? [])]
|
||||
.join(' ')
|
||||
.toLowerCase();
|
||||
return hay.includes(q);
|
||||
});
|
||||
}
|
||||
|
||||
function execSlash(id: string) {
|
||||
|
||||
@@ -8,6 +8,13 @@ export interface BlockType {
|
||||
icon: string;
|
||||
label: string;
|
||||
description: string;
|
||||
/**
|
||||
* Search aliases for the slash-command menu. The filter matches the query
|
||||
* against the label, description, id, and each keyword. Use this for
|
||||
* common abbreviations the label/description wouldn't otherwise cover
|
||||
* (e.g. `h2` for "Heading 2", `hr` for "Divider", `todo` for "Checklist").
|
||||
*/
|
||||
keywords?: string[];
|
||||
/** Only available via slash command (insert), not turn-into (convert) */
|
||||
insertOnly?: boolean;
|
||||
/** Only available via turn-into (convert), not slash command (insert) */
|
||||
@@ -16,17 +23,17 @@ export interface BlockType {
|
||||
|
||||
export const BLOCK_TYPES: BlockType[] = [
|
||||
{ id: 'paragraph', icon: 'Aa', label: 'Text', description: 'Plain text', convertOnly: true },
|
||||
{ id: 'heading1', icon: 'H1', label: 'Heading 1', description: 'Large heading' },
|
||||
{ id: 'heading2', icon: 'H2', label: 'Heading 2', description: 'Medium heading' },
|
||||
{ id: 'heading3', icon: 'H3', label: 'Heading 3', description: 'Small heading' },
|
||||
{ id: 'bulletList', icon: '•', label: 'Bullet List', description: 'Unordered list' },
|
||||
{ id: 'orderedList', icon: '1.', label: 'Numbered List', description: 'Ordered list' },
|
||||
{ id: 'taskList', icon: '☐', label: 'Checklist', description: 'Task list' },
|
||||
{ id: 'codeBlock', icon: '<>', label: 'Code Block', description: 'Fenced code block' },
|
||||
{ id: 'htmlBlock', icon: 'HTML', label: 'HTML Block', description: 'Sanitized HTML embed (live preview)', insertOnly: true },
|
||||
{ id: 'blockquote', icon: '❝', label: 'Blockquote', description: 'Quote block' },
|
||||
{ id: 'horizontalRule', icon: '——', label: 'Divider', description: 'Horizontal rule', insertOnly: true },
|
||||
{ id: 'table', icon: '⊞', label: 'Table', description: '3×3 table', insertOnly: true },
|
||||
{ id: 'heading1', icon: 'H1', label: 'Heading 1', description: 'Large heading', keywords: ['h1'] },
|
||||
{ id: 'heading2', icon: 'H2', label: 'Heading 2', description: 'Medium heading', keywords: ['h2'] },
|
||||
{ id: 'heading3', icon: 'H3', label: 'Heading 3', description: 'Small heading', keywords: ['h3'] },
|
||||
{ id: 'bulletList', icon: '•', label: 'Bullet List', description: 'Unordered list', keywords: ['ul', 'bullets', 'unordered'] },
|
||||
{ id: 'orderedList', icon: '1.', label: 'Numbered List', description: 'Ordered list', keywords: ['ol', 'numbered', 'ordered'] },
|
||||
{ id: 'taskList', icon: '☐', label: 'Checklist', description: 'Task list', keywords: ['todo', 'task', 'checkbox', 'check'] },
|
||||
{ id: 'codeBlock', icon: '<>', label: 'Code Block', description: 'Fenced code block', keywords: ['code', 'pre'] },
|
||||
{ id: 'htmlBlock', icon: 'HTML', label: 'HTML Block', description: 'Sanitized HTML embed (live preview)', insertOnly: true, keywords: ['html', 'embed'] },
|
||||
{ id: 'blockquote', icon: '❝', label: 'Blockquote', description: 'Quote block', keywords: ['quote', 'bq'] },
|
||||
{ id: 'horizontalRule', icon: '——', label: 'Divider', description: 'Horizontal rule', insertOnly: true, keywords: ['hr', 'rule', 'separator'] },
|
||||
{ id: 'table', icon: '⊞', label: 'Table', description: '3×3 table', insertOnly: true, keywords: ['tbl', 'grid'] },
|
||||
];
|
||||
|
||||
/** Block types available in the slash command menu (excludes convert-only types like "Text") */
|
||||
|
||||
Reference in New Issue
Block a user