Skip to content
Snippets Groups Projects
Commit 543d9bee authored by chris's avatar chris
Browse files

display soft breaks

parent 687d6fee
No related branches found
No related tags found
1 merge request!493Fix ai
......@@ -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);
});
......
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;
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;
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
......
......@@ -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 = () => {
......
/* 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;
/* 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;
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 } }));
},
);
};
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment