claritykit-svelte
Version:
A comprehensive Svelte component library focused on accessibility, ADHD-optimized design, developer experience, and full SSR compatibility
248 lines (247 loc) • 9.05 kB
JavaScript
import { Extension } from '@tiptap/core';
import { PluginKey } from '@tiptap/pm/state';
import { Plugin } from '@tiptap/pm/state';
import Suggestion from '@tiptap/suggestion';
export const SlashCommandsExtension = Extension.create({
name: 'slashCommands',
addOptions() {
return {
suggestion: {
items: ({ query }) => {
return this.options.commands
.filter(command => {
if (!command.enabled)
return false;
const searchText = query.toLowerCase();
return (command.title.toLowerCase().includes(searchText) ||
command.description.toLowerCase().includes(searchText) ||
command.keywords.some(keyword => keyword.toLowerCase().includes(searchText)));
})
.slice(0, 10);
},
render: () => {
let component;
let popup;
return {
onStart: (props) => {
// This would render a SlashCommandsList component
console.log('Slash commands started', props);
},
onUpdate: (props) => {
console.log('Slash commands updated', props);
},
onKeyDown: (props) => {
if (props.event.key === 'Escape') {
return true;
}
if (props.event.key === 'ArrowUp') {
// Handle up navigation
return true;
}
if (props.event.key === 'ArrowDown') {
// Handle down navigation
return true;
}
if (props.event.key === 'Enter') {
const selectedCommand = props.items[0]; // Get selected command
if (selectedCommand) {
selectedCommand.command({
editor: props.editor,
range: props.range,
props: props.props,
});
}
return true;
}
return false;
},
onExit: () => {
console.log('Slash commands exited');
},
};
},
char: '/',
allowSpaces: false,
startOfLine: false,
},
commands: [],
};
},
addCommands() {
return {
executeSlashCommand: (commandId, props) => ({ editor, tr, state }) => {
const command = this.options.commands.find(cmd => cmd.id === commandId);
if (command) {
command.command({ editor, range: null, props });
return true;
}
return false;
},
};
},
addProseMirrorPlugins() {
return [
Suggestion({
editor: this.editor,
...this.options.suggestion,
}),
];
},
});
// Default slash commands for chat
export const defaultSlashCommands = [
{
id: 'gif',
title: 'GIF',
description: 'Search and insert a GIF',
icon: '🎬',
keywords: ['gif', 'animation', 'image', 'funny'],
category: 'media',
enabled: true,
command: ({ editor, range }) => {
// This would open a GIF picker
console.log('Opening GIF picker');
if (range) {
editor.chain().focus().deleteRange(range).run();
}
// Insert placeholder or open GIF picker modal
editor.chain().focus().insertContent('<div class="gif-placeholder">🎬 GIF picker would open here</div>').run();
},
},
{
id: 'poll',
title: 'Poll',
description: 'Create an interactive poll',
icon: '📊',
keywords: ['poll', 'vote', 'survey', 'question'],
category: 'interaction',
enabled: true,
command: ({ editor, range }) => {
console.log('Creating poll');
if (range) {
editor.chain().focus().deleteRange(range).run();
}
// Insert poll template
const pollTemplate = `
<div class="poll-block" data-type="poll">
<div class="poll-question" contenteditable="true">What's your question?</div>
<div class="poll-options">
<div class="poll-option" contenteditable="true">Option 1</div>
<div class="poll-option" contenteditable="true">Option 2</div>
</div>
<button class="poll-add-option">+ Add option</button>
</div>
`;
editor.chain().focus().insertContent(pollTemplate).run();
},
},
{
id: 'remind',
title: 'Reminder',
description: 'Set a reminder for this message',
icon: '⏰',
keywords: ['remind', 'reminder', 'schedule', 'later'],
category: 'utility',
enabled: true,
command: ({ editor, range }) => {
console.log('Setting reminder');
if (range) {
editor.chain().focus().deleteRange(range).run();
}
// Insert reminder component
const reminderTemplate = `
<div class="reminder-block" data-type="reminder">
<span class="reminder-icon">⏰</span>
<input type="datetime-local" class="reminder-datetime" />
<span class="reminder-text">Remind me about this message</span>
</div>
`;
editor.chain().focus().insertContent(reminderTemplate).run();
},
},
{
id: 'code',
title: 'Code Block',
description: 'Insert a code block with syntax highlighting',
icon: '💻',
keywords: ['code', 'programming', 'syntax', 'block'],
category: 'formatting',
enabled: true,
command: ({ editor, range }) => {
console.log('Inserting code block');
if (range) {
editor.chain().focus().deleteRange(range).run();
}
// Insert code block
editor.chain().focus().toggleCodeBlock().run();
},
},
{
id: 'table',
title: 'Table',
description: 'Insert a table',
icon: '📋',
keywords: ['table', 'grid', 'data', 'rows', 'columns'],
category: 'formatting',
enabled: true,
command: ({ editor, range }) => {
console.log('Inserting table');
if (range) {
editor.chain().focus().deleteRange(range).run();
}
// Insert 3x3 table
editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run();
},
},
{
id: 'divider',
title: 'Divider',
description: 'Insert a horizontal divider',
icon: '➖',
keywords: ['divider', 'separator', 'line', 'break'],
category: 'formatting',
enabled: true,
command: ({ editor, range }) => {
console.log('Inserting divider');
if (range) {
editor.chain().focus().deleteRange(range).run();
}
editor.chain().focus().setHorizontalRule().run();
},
},
{
id: 'emoji',
title: 'Emoji',
description: 'Insert an emoji',
icon: '😀',
keywords: ['emoji', 'emoticon', 'smiley', 'reaction'],
category: 'media',
enabled: true,
command: ({ editor, range }) => {
console.log('Opening emoji picker');
if (range) {
editor.chain().focus().deleteRange(range).run();
}
// This would open an emoji picker
editor.chain().focus().insertContent('😀').run();
},
},
{
id: 'mention',
title: 'Mention',
description: 'Mention someone in the chat',
icon: '@',
keywords: ['mention', 'user', 'notify', 'at'],
category: 'interaction',
enabled: true,
command: ({ editor, range }) => {
console.log('Triggering mention');
if (range) {
editor.chain().focus().deleteRange(range).run();
}
// Insert @ to trigger mention suggestion
editor.chain().focus().insertContent('@').run();
},
},
];
export default SlashCommandsExtension;