From e999d55dc0b70b05eb94519d42d549f688a07e4a Mon Sep 17 00:00:00 2001 From: chris <kokosias@yahoo.gr> Date: Thu, 16 Mar 2023 18:26:14 +0200 Subject: [PATCH] add new files --- .../ExternalAPIContentService/AnyStyleTool.js | 69 +++++++++++++++++++ .../AnyStyleToolGroupService/AnyStyle.js | 13 ++++ .../AnyStyleToolGroupService.js | 10 +++ .../ExternalAPIContentService.js | 24 +++++++ .../ExternalAPIContentService/anyStyle.css | 5 ++ .../components/ExternalApiButton.js | 58 ++++++++++++++++ .../plugins/AnyStylePlaceHolderPlugin.js | 37 ++++++++++ .../ExternalAPIContentService/replaceText.js | 62 +++++++++++++++++ 8 files changed, 278 insertions(+) create mode 100644 wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleTool.js create mode 100644 wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleToolGroupService/AnyStyle.js create mode 100644 wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleToolGroupService/AnyStyleToolGroupService.js create mode 100644 wax-prosemirror-services/src/ExternalAPIContentService/ExternalAPIContentService.js create mode 100644 wax-prosemirror-services/src/ExternalAPIContentService/anyStyle.css create mode 100644 wax-prosemirror-services/src/ExternalAPIContentService/components/ExternalApiButton.js create mode 100644 wax-prosemirror-services/src/ExternalAPIContentService/plugins/AnyStylePlaceHolderPlugin.js create mode 100644 wax-prosemirror-services/src/ExternalAPIContentService/replaceText.js diff --git a/wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleTool.js b/wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleTool.js new file mode 100644 index 000000000..f3bdb1cd8 --- /dev/null +++ b/wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleTool.js @@ -0,0 +1,69 @@ +import React, { useContext } from 'react'; +import { v4 as uuidv4 } from 'uuid'; +import { isEmpty } from 'lodash'; +import { injectable } from 'inversify'; +import { WaxContext, Commands, Tools } from 'wax-prosemirror-core'; +import ExternalApiButton from './components/ExternalApiButton'; +import replaceText from './replaceText'; + +@injectable() +class AnyStyleTool extends Tools { + title = 'ChatGPT'; + name = 'ChatGPT'; + label = 'ChatGPT'; + + get run() { + return true; + } + + select = activeView => { + return true; + }; + + get enable() { + return state => { + return true; + }; + } + + renderTool(view) { + if (isEmpty(view)) return null; + const context = useContext(WaxContext); + const anyStyle = replaceText( + view, + this.config.get('config.ExternalAPIContentService') + .ExternalAPIContentTransformation, + this.pmplugins.get('anyStylePlaceHolder'), + context, + ); + return this.isDisplayed() ? ( + <ExternalApiButton + anyStyle={anyStyle} + item={this.toJSON()} + key={uuidv4()} + view={view} + /> + ) : null; + } + + // renderTool(view) { + // if (isEmpty(view)) return null; + // const context = useContext(WaxContext); + // const upload = fileUpload( + // view, + // this.config.get('fileUpload'), + // this.pmplugins.get('imagePlaceHolder'), + // context, + // ); + // return this.isDisplayed() ? ( + // <ImageUpload + // fileUpload={upload} + // item={this.toJSON()} + // key={uuidv4()} + // view={view} + // /> + // ) : null; + // } +} + +export default AnyStyleTool; diff --git a/wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleToolGroupService/AnyStyle.js b/wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleToolGroupService/AnyStyle.js new file mode 100644 index 000000000..57217fd67 --- /dev/null +++ b/wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleToolGroupService/AnyStyle.js @@ -0,0 +1,13 @@ +import { injectable, inject } from 'inversify'; +import { ToolGroup } from 'wax-prosemirror-core'; + +@injectable() +class Anystyle extends ToolGroup { + tools = []; + constructor(@inject('AnyStyleTool') anyStyleTool) { + super(); + this.tools = [anyStyleTool]; + } +} + +export default Anystyle; diff --git a/wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleToolGroupService/AnyStyleToolGroupService.js b/wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleToolGroupService/AnyStyleToolGroupService.js new file mode 100644 index 000000000..703a4688e --- /dev/null +++ b/wax-prosemirror-services/src/ExternalAPIContentService/AnyStyleToolGroupService/AnyStyleToolGroupService.js @@ -0,0 +1,10 @@ +import { Service } from 'wax-prosemirror-core'; +import AnyStyle from './AnyStyle'; + +class AnyStyleToolGroupService extends Service { + register() { + this.container.bind('AnyStyle').to(AnyStyle); + } +} + +export default AnyStyleToolGroupService; diff --git a/wax-prosemirror-services/src/ExternalAPIContentService/ExternalAPIContentService.js b/wax-prosemirror-services/src/ExternalAPIContentService/ExternalAPIContentService.js new file mode 100644 index 000000000..5d6012349 --- /dev/null +++ b/wax-prosemirror-services/src/ExternalAPIContentService/ExternalAPIContentService.js @@ -0,0 +1,24 @@ +import { Service } from 'wax-prosemirror-core'; +import AnyStyleTool from './AnyStyleTool'; +import AnyStyleToolGroupService from './AnyStyleToolGroupService/AnyStyleToolGroupService'; +import AnyStylePlaceHolderPlugin from './plugins/AnyStylePlaceHolderPlugin'; +import './anyStyle.css'; + +class ExternalAPIContentService extends Service { + name = 'ExternalAPIContentService'; + + boot() { + this.app.PmPlugins.add( + 'anyStylePlaceHolder', + AnyStylePlaceHolderPlugin('anyStylePlaceHolder'), + ); + } + + register() { + this.container.bind('AnyStyleTool').to(AnyStyleTool); + } + + dependencies = [new AnyStyleToolGroupService()]; +} + +export default ExternalAPIContentService; diff --git a/wax-prosemirror-services/src/ExternalAPIContentService/anyStyle.css b/wax-prosemirror-services/src/ExternalAPIContentService/anyStyle.css new file mode 100644 index 000000000..ef4885cbd --- /dev/null +++ b/wax-prosemirror-services/src/ExternalAPIContentService/anyStyle.css @@ -0,0 +1,5 @@ +placeholder-any-style:before { + position: relative; + top: 3px; + content: url("data:image/svg+xml; utf8, <svg xmlns='http://www.w3.org/2000/svg' height='24' width='24'><path d='M8 20h8v-3q0-1.65-1.175-2.825Q13.65 13 12 13q-1.65 0-2.825 1.175Q8 15.35 8 17Zm-4 2v-2h2v-3q0-1.525.713-2.863Q7.425 12.8 8.7 12q-1.275-.8-1.987-2.138Q6 8.525 6 7V4H4V2h16v2h-2v3q0 1.525-.712 2.862Q16.575 11.2 15.3 12q1.275.8 1.988 2.137Q18 15.475 18 17v3h2v2Z' /></svg>"); +} \ No newline at end of file diff --git a/wax-prosemirror-services/src/ExternalAPIContentService/components/ExternalApiButton.js b/wax-prosemirror-services/src/ExternalAPIContentService/components/ExternalApiButton.js new file mode 100644 index 000000000..2ac05fa10 --- /dev/null +++ b/wax-prosemirror-services/src/ExternalAPIContentService/components/ExternalApiButton.js @@ -0,0 +1,58 @@ +/* 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 ExternalApiButton = ({ view = {}, item, anyStyle }) => { + 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; + /* this is the content that we have to get from the selection */ + const textSelection = new TextSelection($from, $to); + + const content = textSelection.content(); + + anyStyle(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 ExternalApiButtonComponent = useMemo( + () => ( + <MenuButton + active={isActive || false} + disabled={isDisabled} + iconName={icon} + label={label} + onMouseDown={e => handleMouseDown(e, view.state, view.dispatch)} + title={title} + /> + ), + [isActive, isDisabled], + ); + + return ExternalApiButtonComponent; +}; + +export default ExternalApiButton; diff --git a/wax-prosemirror-services/src/ExternalAPIContentService/plugins/AnyStylePlaceHolderPlugin.js b/wax-prosemirror-services/src/ExternalAPIContentService/plugins/AnyStylePlaceHolderPlugin.js new file mode 100644 index 000000000..990b75ee2 --- /dev/null +++ b/wax-prosemirror-services/src/ExternalAPIContentService/plugins/AnyStylePlaceHolderPlugin.js @@ -0,0 +1,37 @@ +/* eslint-disable no-param-reassign */ +import { Plugin, PluginKey } from 'prosemirror-state'; +import { Decoration, DecorationSet } from 'prosemirror-view'; + +export default key => + new Plugin({ + key: new PluginKey(key), + state: { + init: function init() { + return DecorationSet.empty; + }, + apply: function apply(tr, set) { + // Adjust decoration positions to changes made by the transaction + set = set.map(tr.mapping, tr.doc); + // See if the transaction adds or removes any placeholders + const action = tr.getMeta(this); + if (action && action.add) { + const widget = document.createElement('placeholder-any-style'); + const deco = Decoration.widget(action.add.pos, widget, { + id: action.add.id, + }); + + set = set.add(tr.doc, [deco]); + } else if (action && action.remove) { + set = set.remove( + set.find(null, null, spec => spec.id === action.remove.id), + ); + } + return set; + }, + }, + props: { + decorations: function decorations(state) { + return this.getState(state); + }, + }, + }); diff --git a/wax-prosemirror-services/src/ExternalAPIContentService/replaceText.js b/wax-prosemirror-services/src/ExternalAPIContentService/replaceText.js new file mode 100644 index 000000000..e54ae8af2 --- /dev/null +++ b/wax-prosemirror-services/src/ExternalAPIContentService/replaceText.js @@ -0,0 +1,62 @@ +import { v4 as uuidv4 } from 'uuid'; +import { DOMParser } from 'prosemirror-model'; + +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; +}; + +export default ( + view, + ExternalAPIContentTransformation, + 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); + + ExternalAPIContentTransformation(data).then( + text => { + const pos = findPlaceholder(view.state, id, placeholderPlugin); + // If the content around the placeholder has been deleted, drop + // the image + if (pos == null) { + return; + } + const parser = DOMParser.fromSchema( + context.pmViews.main.state.config.schema, + ); + const parsedContent = parser.parse(elementFromString(text)); + // Otherwise, insert it at the placeholder's position, and remove + // the placeholder + context.pmViews[context.activeViewId].dispatch( + context.pmViews[context.activeViewId].state.tr + .replaceWith(pos, pos, parsedContent) + .setMeta(placeholderPlugin, { remove: { id } }), + ); + }, + + () => { + // On failure, just clean up the placeholder + view.dispatch(tr.setMeta(placeholderPlugin, { remove: { id } })); + }, + ); +}; -- GitLab