You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
4.9 KiB
168 lines
4.9 KiB
import type { WordsBlockType, WordsHeadingLevel, WordsListKind, WordsMark, WordsTextAlign } from './document';
|
|
import {
|
|
clearFormatting,
|
|
decreaseIndent,
|
|
deleteBackward,
|
|
deleteForward,
|
|
deleteLineBackward,
|
|
deleteLineForward,
|
|
deleteRange,
|
|
deleteWordBackward,
|
|
deleteWordForward,
|
|
increaseIndent,
|
|
insertLink,
|
|
insertLineBreak,
|
|
insertParagraph,
|
|
insertText,
|
|
replaceDocument,
|
|
setBlock,
|
|
setTextAlign,
|
|
toggleList,
|
|
toggleMark,
|
|
unlink,
|
|
type WordsEditorState,
|
|
type WordsOperationResult
|
|
} from './operations';
|
|
import { renderWordsPlainText } from './render';
|
|
import { parseWordsHtml, serializeWordsHtml } from './serialize-html';
|
|
import { parseWordsJson, serializeWordsJson } from './serialize-json';
|
|
import { parseWordsMarkdown, serializeWordsMarkdown } from './serialize-markdown';
|
|
import { parseWordsPlainText } from './serialize-text';
|
|
import { replaceAllTextInDocument } from './find-replace';
|
|
|
|
export type WordsImportFormat = 'json' | 'text' | 'html' | 'markdown';
|
|
export type WordsExportFormat = 'json' | 'text' | 'html' | 'markdown';
|
|
|
|
export type WordsCommand =
|
|
| { type: 'insertText'; text: string }
|
|
| { type: 'insertParagraph' }
|
|
| { type: 'insertLineBreak' }
|
|
| { type: 'deleteBackward' }
|
|
| { type: 'deleteForward' }
|
|
| { type: 'deleteWordBackward' }
|
|
| { type: 'deleteWordForward' }
|
|
| { type: 'deleteLineBackward' }
|
|
| { type: 'deleteLineForward' }
|
|
| { type: 'deleteRange' }
|
|
| { type: 'toggleMark'; mark: WordsMark }
|
|
| { type: 'clearFormatting' }
|
|
| { type: 'setBlock'; block: Exclude<WordsBlockType, 'list'>; level?: WordsHeadingLevel }
|
|
| { type: 'setTextAlign'; align: WordsTextAlign }
|
|
| { type: 'increaseIndent' }
|
|
| { type: 'decreaseIndent' }
|
|
| { type: 'toggleList'; kind: WordsListKind }
|
|
| { type: 'insertLink'; href: string; title?: string }
|
|
| { type: 'unlink' }
|
|
| { type: 'replaceDocument'; document: unknown }
|
|
| { type: 'replaceAllText'; query: string; replacement: string }
|
|
| { type: 'importContent'; content: string; format: WordsImportFormat }
|
|
| { type: 'exportContent'; format: WordsExportFormat };
|
|
|
|
export function applyWordsCommand(
|
|
state: WordsEditorState,
|
|
command: WordsCommand
|
|
): WordsOperationResult {
|
|
switch (command.type) {
|
|
case 'insertText':
|
|
return insertText(state, command.text);
|
|
case 'insertParagraph':
|
|
return insertParagraph(state);
|
|
case 'insertLineBreak':
|
|
return insertLineBreak(state);
|
|
case 'deleteBackward':
|
|
return deleteBackward(state);
|
|
case 'deleteForward':
|
|
return deleteForward(state);
|
|
case 'deleteWordBackward':
|
|
return deleteWordBackward(state);
|
|
case 'deleteWordForward':
|
|
return deleteWordForward(state);
|
|
case 'deleteLineBackward':
|
|
return deleteLineBackward(state);
|
|
case 'deleteLineForward':
|
|
return deleteLineForward(state);
|
|
case 'deleteRange':
|
|
return deleteRange(state);
|
|
case 'toggleMark':
|
|
return toggleMark(state, command.mark);
|
|
case 'clearFormatting':
|
|
return clearFormatting(state);
|
|
case 'setBlock':
|
|
return setBlock(state, command.block, { level: command.level });
|
|
case 'setTextAlign':
|
|
return setTextAlign(state, command.align);
|
|
case 'increaseIndent':
|
|
return increaseIndent(state);
|
|
case 'decreaseIndent':
|
|
return decreaseIndent(state);
|
|
case 'toggleList':
|
|
return toggleList(state, command.kind);
|
|
case 'insertLink':
|
|
return insertLink(state, command.href, command.title);
|
|
case 'unlink':
|
|
return unlink(state);
|
|
case 'replaceDocument':
|
|
return replaceDocument(state, command.document);
|
|
case 'replaceAllText':
|
|
return replaceAllText(state, command.query, command.replacement);
|
|
case 'importContent':
|
|
return importWordsContent(state, command.content, command.format);
|
|
case 'exportContent':
|
|
return exportWordsContent(state, command.format);
|
|
}
|
|
}
|
|
|
|
function importWordsContent(
|
|
state: WordsEditorState,
|
|
content: string,
|
|
format: WordsImportFormat
|
|
): WordsOperationResult {
|
|
try {
|
|
return replaceDocument(
|
|
state,
|
|
format === 'json'
|
|
? parseWordsJson(content)
|
|
: format === 'html'
|
|
? parseWordsHtml(content)
|
|
: format === 'markdown'
|
|
? parseWordsMarkdown(content)
|
|
: parseWordsPlainText(content)
|
|
);
|
|
} catch {
|
|
return { state, changed: false };
|
|
}
|
|
}
|
|
|
|
function exportWordsContent(
|
|
state: WordsEditorState,
|
|
format: WordsExportFormat
|
|
): WordsOperationResult {
|
|
const exportedContent =
|
|
format === 'json'
|
|
? serializeWordsJson(state.document)
|
|
: format === 'html'
|
|
? serializeWordsHtml(state.document)
|
|
: format === 'markdown'
|
|
? serializeWordsMarkdown(state.document)
|
|
: renderWordsPlainText(state.document);
|
|
return { state, changed: false, exportedContent };
|
|
}
|
|
|
|
function replaceAllText(
|
|
state: WordsEditorState,
|
|
query: string,
|
|
replacement: string
|
|
): WordsOperationResult {
|
|
if (!query) return { state, changed: false };
|
|
const nextDocument = replaceAllTextInDocument(state.document, query, replacement);
|
|
if (nextDocument === state.document) return { state, changed: false };
|
|
return {
|
|
state: {
|
|
document: nextDocument,
|
|
selection: state.selection,
|
|
activeMarks: state.activeMarks
|
|
},
|
|
changed: true
|
|
};
|
|
}
|