Add autoformater for book dialogues

This commit is contained in:
mirivlad 2025-11-28 02:32:24 +08:00
parent b32c26cb04
commit 2aa9b37d4e
1 changed files with 107 additions and 3 deletions

View File

@ -1,3 +1,62 @@
class DialogueFormatter {
constructor(quill) {
this.quill = quill;
this.setupKeyboardHandling();
}
setupKeyboardHandling() {
this.quill.keyboard.addBinding({
key: ' ',
shortKey: false,
handler: (range, context) => {
return this.handleSpacePress(range, context);
}
});
}
handleSpacePress(range, context) {
// Проверяем, достаточно ли символов для проверки
if (range.index < 2) return true;
// Получаем текст от начала строки до текущей позиции
const lineStart = this.getLineStart(range.index);
const textBeforeCursor = this.quill.getText(lineStart, range.index - lineStart);
// Проверяем, начинается ли строка с "-" и пробел будет первым пробелом после дефиса
if (this.isBeginningOfLine(lineStart, range.index) &&
textBeforeCursor === '-') {
// Сохраняем текущую позицию курсора
const savedPosition = range.index;
// Заменяем дефис на тире, сохраняя пробел
this.quill.deleteText(range.index - 1, 2, 'user');
this.quill.insertText(range.index - 1, '— ', 'user');
// Восстанавливаем курсор после пробела
setTimeout(() => {
this.quill.setSelection(savedPosition + 2, 0, 'silent');
}, 0);
return true; // Разрешаем стандартную обработку пробела
}
return true;
}
getLineStart(index) {
let lineStart = index;
while (lineStart > 0 && this.quill.getText(lineStart - 1, 1) !== '\n') {
lineStart--;
}
return lineStart;
}
isBeginningOfLine(lineStart, currentIndex) {
return currentIndex === lineStart + 1;
}
}
class WriterEditor {
constructor(formSelector = '#chapter-form', editorContainerId = 'quill-editor', textareaId = 'content') {
this.form = document.querySelector(formSelector);
@ -15,6 +74,8 @@ class WriterEditor {
toolbar: [
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
['bold','italic','underline','strike'],
[{ 'align': [] }],
[{ 'color': [] }, { 'background': [] }],
['blockquote','code-block'],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'script': 'sub'}, { 'script': 'super' }],
@ -22,17 +83,40 @@ class WriterEditor {
[{ 'size': ['small', false, 'large', 'huge'] }],
[{ 'font': [] }],
['link','image','video'],
['clean']
['clean'],
],
history: { delay: 1000, maxStack: 100, userOnly: true }
history: { delay: 1000, maxStack: 100, userOnly: true },
keyboard: {
bindings: {
// Отключаем автоформатирование списков для дефиса с пробелом
'list autofill': {
key: ' ',
format: ['list'],
handler: function(range, context) {
// Если это начало строки с дефисом - не создаем список
if (context.prefix && context.prefix.trim() === '-') {
return true; // Пропускаем автоформатирование
}
// Стандартная обработка для других случаев
return Quill.import('modules/keyboard').bindings['list autofill'].handler.call(this, range, context);
}
}
}
}
},
placeholder: 'Введите текст главы...'
});
// Инициализируем автоформатер диалогов
new DialogueFormatter(this.quill);
// Загружаем текст
const rawContent = this.editorContainer.dataset.content || '';
if (rawContent.trim()) this.quill.root.innerHTML = rawContent.trim();
// Обрабатываем уже существующие диалоги при загрузке
setTimeout(() => this.formatExistingDialogues(), 100);
// Синхронизация с textarea
const sync = () => {
let html = this.quill.root.innerHTML;
@ -47,8 +131,28 @@ class WriterEditor {
window.quillEditorInstance = this.quill;
window.quillTextarea = this.textarea;
}
// Форматирование уже существующих диалогов при загрузке
formatExistingDialogues() {
const text = this.quill.getText();
const lines = text.split('\n');
let totalOffset = 0;
lines.forEach((line, index) => {
if (line.startsWith('- ')) {
// Находим позицию для замены
const replacePosition = totalOffset;
// Заменяем дефис на тире
this.quill.deleteText(replacePosition, 1, 'silent');
this.quill.insertText(replacePosition, '—', 'silent');
}
totalOffset += line.length + 1; // +1 для символа новой строки
});
}
}
document.addEventListener('DOMContentLoaded', () => {
window.writerEditor = new WriterEditor();
});
});