From 543d9beeb24f59b651c76082a393418bfba96cc3 Mon Sep 17 00:00:00 2001 From: chris <kokosias@yahoo.gr> Date: Thu, 28 Sep 2023 00:52:32 +0300 Subject: [PATCH] display soft breaks --- editors/demo/src/Editoria/config/config.js | 2 +- .../src/AiService/AskAiContentService.js | 7 +- .../src/AiService/AskAiContentTool.js | 41 -------- .../src/AiService/ReplaceSelectedText.js | 50 +++++----- .../src/AiService/components/AskAIOverlay.js | 3 +- .../src/AiService/components/AskAiButton.js | 57 ----------- .../AiService/components/AskAiComponent.js | 31 ------ .../src/AiService/replaceText.js | 97 ------------------- 8 files changed, 29 insertions(+), 259 deletions(-) delete mode 100644 wax-prosemirror-services/src/AiService/AskAiContentTool.js delete mode 100644 wax-prosemirror-services/src/AiService/components/AskAiButton.js delete mode 100644 wax-prosemirror-services/src/AiService/components/AskAiComponent.js delete mode 100644 wax-prosemirror-services/src/AiService/replaceText.js diff --git a/editors/demo/src/Editoria/config/config.js b/editors/demo/src/Editoria/config/config.js index 3c828db6d..e8214f1da 100644 --- a/editors/demo/src/Editoria/config/config.js +++ b/editors/demo/src/Editoria/config/config.js @@ -106,7 +106,7 @@ async function DummyPromise(userInput) { if (userInput === 'reject') { reject('Your request could not be processed for now'); } else { - resolve(text); + resolve('hello how are you?'); } }, 150); }); diff --git a/wax-prosemirror-services/src/AiService/AskAiContentService.js b/wax-prosemirror-services/src/AiService/AskAiContentService.js index 5d8ec89bb..86f3e5553 100644 --- a/wax-prosemirror-services/src/AiService/AskAiContentService.js +++ b/wax-prosemirror-services/src/AiService/AskAiContentService.js @@ -1,5 +1,4 @@ import { Service } from 'wax-prosemirror-core'; -import AskAiContentTool from './AskAiContentTool'; import AskAIOverlay from './components/AskAIOverlay'; import AskAiSelectionPlugin from './plugins/AskAiSelectionPlugin'; import './AskAiContent.css'; @@ -14,7 +13,7 @@ class AskAiContentService extends Service { ); const createOverlay = this.container.get('CreateOverlay'); - const config = this.config; + const { config } = this; // Create the overlay createOverlay( @@ -29,9 +28,7 @@ class AskAiContentService extends Service { ); } - register() { - this.container.bind('AskAiContentTool').to(AskAiContentTool); - } + register() {} } export default AskAiContentService; diff --git a/wax-prosemirror-services/src/AiService/AskAiContentTool.js b/wax-prosemirror-services/src/AiService/AskAiContentTool.js deleted file mode 100644 index 1a6bb73d6..000000000 --- a/wax-prosemirror-services/src/AiService/AskAiContentTool.js +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { v4 as uuidv4 } from 'uuid'; -import { injectable } from 'inversify'; -import { Commands, Tools } from 'wax-prosemirror-core'; -import AskAiComponent from './components/AskAiComponent'; - -@injectable() -class AskAiContentTool extends Tools { - title = 'ChatGPT'; - name = 'ChatGPT'; - label = ''; - - get run() { - return true; - } - - get enable() { - return state => { - return Commands.isOnSameTextBlock(state); - }; - } - - select = state => { - return Commands.isOnSameTextBlock(state); - }; - - renderTool(view) { - return ( - <AskAiComponent - config={this.config} - displayed={this.isDisplayed()} - item={this} - key={uuidv4()} - pmplugins={this.pmplugins} - view={view} - /> - ); - } -} - -export default AskAiContentTool; diff --git a/wax-prosemirror-services/src/AiService/ReplaceSelectedText.js b/wax-prosemirror-services/src/AiService/ReplaceSelectedText.js index 0cb31337c..34f04ad17 100644 --- a/wax-prosemirror-services/src/AiService/ReplaceSelectedText.js +++ b/wax-prosemirror-services/src/AiService/ReplaceSelectedText.js @@ -1,53 +1,48 @@ import { DOMParser } from 'prosemirror-model'; -import { ReplaceStep, ReplaceAroundStep } from 'prosemirror-transform'; -import { Selection, TextSelection } from 'prosemirror-state'; +import { TextSelection } from 'prosemirror-state'; -// To Do keep soft break const elementFromString = string => { const wrappedValue = `<body>${string}</body>`; return new window.DOMParser().parseFromString(wrappedValue, 'text/html').body; }; -const replaceSelectedText = (view, transformedText) => { +const replaceSelectedText = (view, transformedText, replace = false) => { let { state } = view; let { tr } = state; - const { from, to } = tr.selection; + const paragraphNodes = []; + const parser = DOMParser.fromSchema(state.config.schema); - // Check if 'from' and 'to' are within the document size if (from > state.doc.content.size || to > state.doc.content.size) { console.error('Position out of range'); return; } - // Delete the selected text if any - if (from !== to) { - tr = tr.delete(from, to); - } - - // Fetch the most recent state again - state = view.state; - - const paragraphNodes = []; - if (transformedText.includes('\n\n')) { transformedText.split('\n\n').forEach(element => { paragraphNodes.push( - state.schema.nodes.paragraph.create({}, state.schema.text(element)), + parser.parse(elementFromString(element.replace(/\n/g, '<br />')), { + preserveWhitespace: true, + }), ); }); } - const newText = state.schema.text(transformedText); - const finalReplacementText = - paragraphNodes.length !== 0 ? paragraphNodes : newText; - - // Replace the selected text with the new text - tr = tr.replaceWith(from, from, finalReplacementText); // Note: 'to' is replaced with 'from' + paragraphNodes.length !== 0 + ? paragraphNodes + : state.schema.text(transformedText); + + if (replace) { + if (from !== to) { + tr = tr.delete(from, to); + } + tr = tr.replaceWith(from, from, finalReplacementText); + } else { + tr = tr.insert(to, finalReplacementText); + } - // Dispatch the transaction to update the state view.dispatch(tr); // Fetch the most recent state again @@ -55,7 +50,12 @@ const replaceSelectedText = (view, transformedText) => { // Update the selection to the end of the new text const newTo = from + transformedText.length; - const newSelection = TextSelection.create(state.doc, newTo + 2, newTo + 2); + const cursorPosition = paragraphNodes.length !== 0 ? newTo + 2 : newTo; + const newSelection = TextSelection.create( + state.doc, + cursorPosition, + cursorPosition, + ); tr = state.tr.setSelection(newSelection); // Dispatch the final transaction to update the state diff --git a/wax-prosemirror-services/src/AiService/components/AskAIOverlay.js b/wax-prosemirror-services/src/AiService/components/AskAIOverlay.js index 1b92a26a0..cc697b9b6 100644 --- a/wax-prosemirror-services/src/AiService/components/AskAIOverlay.js +++ b/wax-prosemirror-services/src/AiService/components/AskAIOverlay.js @@ -158,7 +158,6 @@ const AskAIOverlay = ({ setPosition, position, config }) => { try { const response = await AskAiContentTransformation(combinedInput); - console.log(response); setResult(response); setIsSubmitted(true); } catch (error) { @@ -170,7 +169,7 @@ const AskAIOverlay = ({ setPosition, position, config }) => { }; const handleReplaceText = () => { - replaceSelectedText(activeView, result); + replaceSelectedText(activeView, result, true); }; const discardResults = () => { diff --git a/wax-prosemirror-services/src/AiService/components/AskAiButton.js b/wax-prosemirror-services/src/AiService/components/AskAiButton.js deleted file mode 100644 index dfdb32275..000000000 --- a/wax-prosemirror-services/src/AiService/components/AskAiButton.js +++ /dev/null @@ -1,57 +0,0 @@ -/* eslint react/prop-types: 0 */ -import React, { useContext, useMemo, useEffect } from 'react'; -import { WaxContext, DocumentHelpers, MenuButton } from 'wax-prosemirror-core'; -import { TextSelection } from 'prosemirror-state'; - -const AskAiButton = ({ view = {}, item, AskAiContent }) => { - const { active, icon, label, run, select, title } = item; - - const { - app, - pmViews: { main }, - activeViewId, - activeView, - } = useContext(WaxContext); - - const { state } = view; - - const handleMouseDown = (e, editorState) => { - e.preventDefault(); - const { - selection: { $from, $to }, - } = editorState; - const textSelection = new TextSelection($from, $to); - - const content = textSelection.content(); - - AskAiContent(content.content.content[0].textContent); - }; - - useEffect(() => {}, []); - - const isActive = !!active(state, activeViewId); - let isDisabled = !select(state, activeViewId, activeView); - - const isEditable = main.props.editable(editable => { - return editable; - }); - if (!isEditable) isDisabled = true; - - const AskAiButtonComponent = useMemo( - () => ( - <MenuButton - active={isActive || false} - disabled={isDisabled} - iconName={icon} - label={label} - onMouseDown={e => handleMouseDown(e, view.state, view.dispatch)} - title={title} - /> - ), - [isActive, isDisabled], - ); - - return AskAiButtonComponent; -}; - -export default AskAiButton; diff --git a/wax-prosemirror-services/src/AiService/components/AskAiComponent.js b/wax-prosemirror-services/src/AiService/components/AskAiComponent.js deleted file mode 100644 index f41275dbc..000000000 --- a/wax-prosemirror-services/src/AiService/components/AskAiComponent.js +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-disable react/prop-types */ -import React, { useContext } from 'react'; -import { v4 as uuidv4 } from 'uuid'; -import { WaxContext } from 'wax-prosemirror-core'; -import { isEmpty } from 'lodash'; -import replaceText from '../replaceText'; -import AskAiButton from './AskAiButton'; - -const AskAiComponent = ({ view, displayed, config, pmplugins, item }) => { - const context = useContext(WaxContext); - if (isEmpty(view)) return null; - - const AskAiContent = replaceText( - view, - config.get('config.AskAiContentService') - .AskAiContentTransformation, - pmplugins.get('AskAiContentPlaceHolder'), - context, - ); - - return displayed ? ( - <AskAiButton - AskAiContent={AskAiContent} - item={item.toJSON()} - key={uuidv4()} - view={view} - /> - ) : null; -}; - -export default AskAiComponent; diff --git a/wax-prosemirror-services/src/AiService/replaceText.js b/wax-prosemirror-services/src/AiService/replaceText.js deleted file mode 100644 index ca63d2574..000000000 --- a/wax-prosemirror-services/src/AiService/replaceText.js +++ /dev/null @@ -1,97 +0,0 @@ -import { DOMParser } from 'prosemirror-model'; -import { ReplaceStep, ReplaceAroundStep } from 'prosemirror-transform'; -import { Selection } from 'prosemirror-state'; - -const findPlaceholder = (state, id, placeholderPlugin) => { - const decos = placeholderPlugin.getState(state); - const found = decos.find(null, null, spec => spec.id === id); - return found.length ? found[0].from : null; -}; - -const elementFromString = string => { - const wrappedValue = `<body>${string}</body>`; - - return new window.DOMParser().parseFromString(wrappedValue, 'text/html').body; -}; - -const selectionToInsertionEnd = (tr, startLen, bias) => { - const last = tr.steps.length - 1; - - if (last < startLen) { - return; - } - - const step = tr.steps[last]; - - if (!(step instanceof ReplaceStep || step instanceof ReplaceAroundStep)) { - return; - } - - const map = tr.mapping.maps[last]; - let end = 0; - - map.forEach((_from, _to, _newFrom, newTo) => { - if (end === 0) { - end = newTo; - } - }); - tr.setSelection(Selection.near(tr.doc.resolve(end), bias)); -}; - -export default ( - view, - AskAiContentTransformation, - placeholderPlugin, - context, -) => data => { - const { state } = view; - // A fresh object to act as the ID for this upload - const id = {}; - - // Replace the selection with a placeholder - const { tr } = state; - if (!tr.selection.empty) tr.deleteSelection(); - - tr.setMeta(placeholderPlugin, { - add: { id, pos: tr.selection.from }, - }); - - view.dispatch(tr); - - AskAiContentTransformation(data).then( - text => { - const pos = findPlaceholder(view.state, id, placeholderPlugin); - - if (pos == null) { - return; - } - const parser = DOMParser.fromSchema( - context.pmViews.main.state.config.schema, - ); - const options = - text.includes('<ul>') || text.includes('ol') - ? {} - : { - preserveWhitespace: 'full', - }; - const parsedContent = parser.parse( - elementFromString(text.replace(/^\s+|\s+$/g, '')), - options, - ); - - const newTr = context.pmViews.main.state.tr; - - newTr - .replaceWith(pos - 1, pos - 1, parsedContent) - .setMeta(placeholderPlugin, { remove: { id } }); - - selectionToInsertionEnd(newTr, newTr.steps.length - 1, 1); - context.pmViews.main.dispatch(newTr); - }, - - () => { - // On failure, just clean up the placeholder - view.dispatch(tr.setMeta(placeholderPlugin, { remove: { id } })); - }, - ); -}; -- GitLab