diff --git a/editors/demo/src/Editoria/config/config.js b/editors/demo/src/Editoria/config/config.js
index a11306ffd7d8e5dd138a1816c8b6b3ef7d357c13..aa5d9607b8066939535cbbd0b45742c3f1847362 100644
--- a/editors/demo/src/Editoria/config/config.js
+++ b/editors/demo/src/Editoria/config/config.js
@@ -45,6 +45,7 @@ import {
   // YjsService,
   // BlockDropDownToolGroupService,
   // TitleToolGroupService,
+  AskAiContentService,
 } from 'wax-prosemirror-services';
 
 import { TablesService, tableEditing, columnResizing } from 'wax-table-service';
@@ -63,6 +64,21 @@ import CharactersList from './CharactersList';
 //   console.log(title);
 // };
 
+async function DummyPromise(userInput) {
+  return new Promise((resolve, reject) => {
+    setTimeout(() => {
+      console.log('User input:', userInput);
+      if (userInput === 'reject') {
+        reject('Your request could not be processed for now');
+      } else {
+        resolve(
+          'He made significant contributions to theoretical physics, including achievements in quantum mechanics',
+        );
+      }
+    }, 4150);
+  });
+}
+
 const updateTitle = debounce(title => {
   console.log(title);
 }, 100);
@@ -176,10 +192,15 @@ export default {
   //   docIdentifier: 'prosemirror-demo',
   // },
 
+  AskAiContentService: {
+    AskAiContentTransformation: DummyPromise,
+  },
+
   services: [
     // new TitleToolGroupService(),
     // new YjsService(),
     // new BlockDropDownToolGroupService(),
+    new AskAiContentService(),
     new CustomTagService(),
     new DisplayBlockLevelService(),
     new DisplayToolGroupService(),
diff --git a/wax-prosemirror-core/src/components/icons/icons.js b/wax-prosemirror-core/src/components/icons/icons.js
index a95127cbfa2352cbde81f58cf61973aa9f7c9488..9a8cc58049144061b930a81c45e297fdb03e7002 100644
--- a/wax-prosemirror-core/src/components/icons/icons.js
+++ b/wax-prosemirror-core/src/components/icons/icons.js
@@ -224,12 +224,12 @@ export default {
   highlight: ({ className }) => (
     <Svg className={className} viewBox="0 0 24 24">
       <path
-        id="highlight"
         d="M14.837,2.538L7.587,9.617L6.826,11.846L5.707,12.94L8.752,15.912L9.869,14.818L12.153,14.075L19.402,6.995L14.837,2.538ZM21.685,6.252C22.105,6.662 22.105,7.328 21.685,7.738L13.315,15.912L11.033,16.655L9.511,18.141C9.091,18.551 8.41,18.551 7.989,18.141L3.424,13.682C3.004,13.272 3.004,12.607 3.424,12.197L4.945,10.711L5.706,8.482L14.076,0.308C14.497,-0.103 15.178,-0.103 15.598,0.308L21.685,6.252ZM14.837,5.509L16.359,6.995L10.46,12.769L8.939,11.283L14.837,5.509ZM6.468,18L4.566,20L0,18.514L3.424,15.027"
+        id="highlight"
       />
       <path
-        id="trait"
         d="M7.447,18.31L7.64,18.499C8.254,19.097 9.248,19.097 9.86,18.499L10.053,18.31L22.506,18.31L22.506,21.417L1.876,21.417L1.876,19.65L4.411,20.475C4.594,20.535 4.796,20.484 4.928,20.345L6.83,18.345L6.794,18.31L7.447,18.31Z"
+        id="trait"
       />
     </Svg>
   ),
@@ -412,7 +412,7 @@ export default {
     </Svg>
   ),
   IconCross: ({ className }) => (
-    <Svg className={className} viewBox="0 0 409.6 409.6" fill="none">
+    <Svg className={className} fill="none" viewBox="0 0 409.6 409.6">
       <path d="m405.332031 192h-170.664062v-170.667969c0-11.773437-9.558594-21.332031-21.335938-21.332031-11.773437 0-21.332031 9.558594-21.332031 21.332031v170.667969h-170.667969c-11.773437 0-21.332031 9.558594-21.332031 21.332031 0 11.777344 9.558594 21.335938 21.332031 21.335938h170.667969v170.664062c0 11.777344 9.558594 21.335938 21.332031 21.335938 11.777344 0 21.335938-9.558594 21.335938-21.335938v-170.664062h170.664062c11.777344 0 21.335938-9.558594 21.335938-21.335938 0-11.773437-9.558594-21.332031-21.335938-21.332031zm0 0" />
     </Svg>
   ),
@@ -503,4 +503,127 @@ export default {
       <path d="M478-240q21 0 35.5-14.5T528-290q0-21-14.5-35.5T478-340q-21 0-35.5 14.5T428-290q0 21 14.5 35.5T478-240Zm-36-154h74q0-33 7.5-52t42.5-52q26-26 41-49.5t15-56.5q0-56-41-86t-97-30q-57 0-92.5 30T342-618l66 26q5-18 22.5-39t53.5-21q32 0 48 17.5t16 38.5q0 20-12 37.5T506-526q-44 39-54 59t-10 73Zm38 314q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z" />
     </Svg>
   ),
+  deleteIco: ({ className }) => (
+    <svg
+      className={className}
+      fill="none"
+      height="13"
+      viewBox="0 0 20 20"
+      width="20"
+      xmlns="http://www.w3.org/2000/svg"
+    >
+      <title>Delete Icon</title>
+      <path
+        d="M1.65222 5.47827H18.3479"
+        stroke="#FF4E4E"
+        strokeLinecap="round"
+      />
+      <path
+        d="M5.82666 5.47826V3C5.82666 2.44771 6.27438 2 6.82666 2H13.1745C13.7268 2 14.1745 2.44772 14.1745 3V5.47826"
+        stroke="#FF4E4E"
+        strokeLinecap="round"
+        strokeLinejoin="round"
+      />
+      <path
+        d="M3.73889 5.47827L4.38207 17.0555C4.41151 17.5854 4.8498 18 5.38053 18H14.619C15.1497 18 15.588 17.5854 15.6174 17.0555L16.2606 5.47827"
+        stroke="#FF4E4E"
+        strokeLinecap="round"
+        strokeLinejoin="round"
+      />
+    </svg>
+  ),
+  tryAgain: ({ className }) => (
+    <svg
+      className={className}
+      fill="none"
+      height="13"
+      viewBox="0 0 20 20"
+      width="20"
+      xmlns="http://www.w3.org/2000/svg"
+    >
+      <title>Try Again Icon</title>
+      <path
+        d="M16.2963 13.0625C16.7471 12.1375 17 11.0983 17 10C17 6.13401 13.866 3 10 3C6.13401 3 3 6.13401 3 10C3 13.866 6.13401 17 10 17C10.8587 17 11.6812 16.8454 12.4414 16.5625"
+        stroke="#595959"
+      />
+      <path d="M18 14L15 12V15L18 14Z" fill="#595959" stroke="#595959" />
+    </svg>
+  ),
+  insertIco: ({ className }) => (
+    <svg
+      className={className}
+      fill="none"
+      height="13"
+      viewBox="0 0 20 20"
+      width="20"
+      xmlns="http://www.w3.org/2000/svg"
+    >
+      <title>Insert Icon</title>
+      <path d="M4 4H16" stroke="#505050" />
+      <path d="M4 7H16" stroke="#505050" />
+      <path d="M7 10L16 10" stroke="#505050" />
+      <path d="M10 13L16 13" stroke="#505050" />
+      <path d="M4 9.5V13H7.5" stroke="#505050" />
+      <path d="M6 11L8 13L6 15" stroke="#505050" />
+    </svg>
+  ),
+  replaceIco: ({ className }) => (
+    <svg
+      className={className}
+      fill="none"
+      height="13"
+      viewBox="0 0 20 20"
+      width="20"
+      xmlns="http://www.w3.org/2000/svg"
+    >
+      <title>Replace</title>
+      <path d="M18 8H2L6 4" stroke="#595959" strokeLinejoin="round" />
+      <path d="M2 12H18L14 16" stroke="#595959" strokeLinejoin="round" />
+    </svg>
+  ),
+  submitIco: ({ className }) => (
+    <svg
+      className={className}
+      fill="none"
+      height="12"
+      viewBox="0 0 13 12"
+      width="13"
+      xmlns="http://www.w3.org/2000/svg"
+    >
+      <title>Submit</title>
+      <path d="M0 11H8V1" stroke="#434343" />
+      <path d="M3.5 4L8 1L12 4" stroke="#434343" />
+    </svg>
+  ),
+  loaderIco: ({ className }) => (
+    <svg
+      className={className}
+      height="17"
+      viewBox="0 0 100 100"
+      width="17"
+      xmlns="http://www.w3.org/2000/svg"
+    >
+      <circle
+        cx="50"
+        cy="50"
+        fill="none"
+        r="40"
+        stroke="#434343"
+        strokeWidth="10"
+      >
+        <animate
+          attributeName="stroke-dasharray"
+          dur="1.5s"
+          repeatCount="indefinite"
+          values="0,150;120,150"
+        />
+        <animate
+          attributeName="stroke-dashoffset"
+          dur="1.5s"
+          repeatCount="indefinite"
+          values="0;-120"
+        />
+      </circle>
+    </svg>
+  ),
 };
diff --git a/wax-prosemirror-services/index.js b/wax-prosemirror-services/index.js
index 47ca65e64e54e53b8e39e16f8cb81719d5f210d6..6eb076f571da213e395186ac9ea94eb56cdc17a6 100644
--- a/wax-prosemirror-services/index.js
+++ b/wax-prosemirror-services/index.js
@@ -31,6 +31,7 @@ export { default as EnterService } from './src/EnterService/EnterService';
 export { default as OENContainersService } from './src/OENContainersService/OENContainersService';
 export { default as YjsService } from './src/YjsService/YjsService';
 export { default as ExternalAPIContentService } from './src/ExternalAPIContentService/ExternalAPIContentService';
+export { default as AskAiContentService } from './src/AiService/AskAiContentService';
 /*
 ToolGroups
 */
diff --git a/wax-prosemirror-services/src/AiService/AskAiContent.css b/wax-prosemirror-services/src/AiService/AskAiContent.css
new file mode 100644
index 0000000000000000000000000000000000000000..590ad236b1218302ad7651450850f6f5adcb0335
--- /dev/null
+++ b/wax-prosemirror-services/src/AiService/AskAiContent.css
@@ -0,0 +1,12 @@
+.fade-in {
+  opacity: 0;
+  transition: opacity 0.5s ease-in-out;
+}
+
+.fade-in.show {
+  opacity: 1;
+}
+
+.ask-ai-selection {
+   background-color: #C5D7FE;
+}
\ No newline at end of file
diff --git a/wax-prosemirror-services/src/AiService/AskAiContentService.js b/wax-prosemirror-services/src/AiService/AskAiContentService.js
new file mode 100644
index 0000000000000000000000000000000000000000..5d8ec89bb34060d632b891dc37ea05fd30b25687
--- /dev/null
+++ b/wax-prosemirror-services/src/AiService/AskAiContentService.js
@@ -0,0 +1,37 @@
+import { Service } from 'wax-prosemirror-core';
+import AskAiContentTool from './AskAiContentTool';
+import AskAIOverlay from './components/AskAIOverlay';
+import AskAiSelectionPlugin from './plugins/AskAiSelectionPlugin';
+import './AskAiContent.css';
+
+class AskAiContentService extends Service {
+  name = 'AskAiContentService';
+
+  boot() {
+    this.app.PmPlugins.add(
+      'askAiSelectionPlugin',
+      AskAiSelectionPlugin('askAiSelectionPlugin'),
+    );
+
+    const createOverlay = this.container.get('CreateOverlay');
+    const config = this.config;
+
+    // Create the overlay
+    createOverlay(
+      AskAIOverlay,
+      { config },
+      {
+        nodeType: '',
+        markType: '',
+        followCursor: false,
+        selection: true,
+      },
+    );
+  }
+
+  register() {
+    this.container.bind('AskAiContentTool').to(AskAiContentTool);
+  }
+}
+
+export default AskAiContentService;
diff --git a/wax-prosemirror-services/src/AiService/AskAiContentTool.js b/wax-prosemirror-services/src/AiService/AskAiContentTool.js
new file mode 100644
index 0000000000000000000000000000000000000000..1a6bb73d61cadf0e951443dc45af1aad9cb8b460
--- /dev/null
+++ b/wax-prosemirror-services/src/AiService/AskAiContentTool.js
@@ -0,0 +1,41 @@
+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/InsertTextBelowSelection.js b/wax-prosemirror-services/src/AiService/InsertTextBelowSelection.js
new file mode 100644
index 0000000000000000000000000000000000000000..ac3e907c7805abf7afdfdcadea98971a36d6be95
--- /dev/null
+++ b/wax-prosemirror-services/src/AiService/InsertTextBelowSelection.js
@@ -0,0 +1,39 @@
+import { TextSelection } from 'prosemirror-state';
+export const insertTextBelowSelection = (view, transformedText) => {
+  let state = view.state;
+  let tr = state.tr;
+
+  const { to } = tr.selection;
+
+  // Check if 'to' is within the document size
+  if (to > state.doc.content.size) {
+    console.error("Position out of range");
+    return;
+  }
+
+  // Fetch the most recent state again
+  state = view.state;
+
+  // Create a new paragraph node with the transformed text
+  const paragraph = state.schema.nodes.paragraph.create(
+    {},
+    state.schema.text(transformedText),
+  );
+
+  // Insert the new paragraph node below the selection
+  tr = tr.insert(to + 1, paragraph);
+
+  // Dispatch the transaction to update the state
+  view.dispatch(tr);
+
+  // Fetch the most recent state again
+  state = view.state;
+
+  // Update the selection to the end of the new text
+  const newTo = to + transformedText.length + 1; // +1 for the paragraph node
+  const newSelection = TextSelection.create(state.doc, newTo, newTo);
+  tr = state.tr.setSelection(newSelection);
+
+  // Dispatch the final transaction to update the state
+  view.dispatch(tr);
+};
diff --git a/wax-prosemirror-services/src/AiService/ReplaceSelectedText.js b/wax-prosemirror-services/src/AiService/ReplaceSelectedText.js
new file mode 100644
index 0000000000000000000000000000000000000000..b0b5bb5eebf5d30359f8d64d6cd20ba2429d6d1d
--- /dev/null
+++ b/wax-prosemirror-services/src/AiService/ReplaceSelectedText.js
@@ -0,0 +1,41 @@
+import { TextSelection } from 'prosemirror-state';
+export const replaceSelectedText = (view, transformedText) => {
+  let state = view.state;
+  let tr = state.tr;
+
+  const { from, to } = tr.selection;
+
+  // 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;
+
+  // Create a new text node with the transformed text
+  const newText = state.schema.text(transformedText);
+
+  // Replace the selected text with the new text
+  tr = tr.replaceWith(from, from, newText);  // Note: 'to' is replaced with 'from'
+
+  // Dispatch the transaction to update the state
+  view.dispatch(tr);
+
+  // Fetch the most recent state again
+  state = view.state;
+
+  // Update the selection to the end of the new text
+  const newTo = from + transformedText.length;
+  const newSelection = TextSelection.create(state.doc, newTo, newTo);
+  tr = state.tr.setSelection(newSelection);
+
+  // Dispatch the final transaction to update the state
+  view.dispatch(tr);
+};
diff --git a/wax-prosemirror-services/src/AiService/components/AskAIOverlay.js b/wax-prosemirror-services/src/AiService/components/AskAIOverlay.js
new file mode 100644
index 0000000000000000000000000000000000000000..0f403323eca10c334c07906e2db136ed952baa9c
--- /dev/null
+++ b/wax-prosemirror-services/src/AiService/components/AskAIOverlay.js
@@ -0,0 +1,252 @@
+import React, {
+  useRef,
+  useEffect,
+  useLayoutEffect,
+  useContext,
+  useState,
+} from 'react';
+import styled from 'styled-components';
+import { WaxContext, icons } from 'wax-prosemirror-core';
+import { replaceSelectedText } from '../ReplaceSelectedText';
+import { insertTextBelowSelection } from '../InsertTextBelowSelection';
+
+const ActionButton = styled.button`
+  align-items: center;
+  align-self: stretch;
+  background: white;
+  border: 0.5px #f0f0f0 solid;
+  cursor: pointer;
+  display: inline-flex;
+  gap: 8px;
+  justify-content: flex-start;
+  padding: 8px 12px;
+`;
+
+const ActionSection = styled.div`
+  align-items: flex-start;
+  box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.04);
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-start;
+  width: 188px;
+`;
+
+const ActionText = styled.div`
+  color: ${props => props.color || '#434343'};
+  font-family: 'Helvetica Neue', sans-serif;
+  font-size: 14px;
+  font-weight: 400;
+  line-height: 22px;
+  word-wrap: break-word;
+`;
+
+const AskAIForm = styled.div`
+  align-items: center;
+  background: #fafafa;
+  border: 0.5px #dcdcdc solid;
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+  box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.08);
+  display: inline-flex;
+  gap: 10px;
+  justify-content: space-between;
+  padding: 8px 12px;
+  width: 458px;
+`;
+
+const AskAIFormInput = styled.input`
+  background: transparent;
+  border: none;
+  color: #000;
+  font-family: 'Helvetica Neue', sans-serif;
+  font-size: 14px;
+  font-weight: 400;
+  line-height: 22px;
+  outline: none;
+  width: 100%;
+`;
+
+const ResultDiv = styled.div`
+  align-items: center;
+  background: white;
+  border: 0.5px #dcdcdc solid;
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+  box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.08);
+  display: inline-flex;
+  flex-direction: column;
+  gap: 10px;
+  justify-content: flex-start;
+  max-height: 200px;
+  overflow-y: auto;
+  padding: 8px 12px;
+  width: 458px;
+`;
+
+const ResultText = styled.div`
+  color: black;
+  flex: 1 1 0;
+  font-family: Roboto, sans-serif;
+  font-size: 14px;
+  font-weight: 400;
+  line-height: 22px;
+  word-wrap: break-word;
+`;
+
+const SubmitButton = styled.button`
+  background: none;
+  border: none;
+  cursor: pointer;
+  outline: none;
+  padding: 0 8px; /* Adjust padding as needed */
+`;
+
+const AskAIOverlay = ({ setPosition, position, config }) => {
+  const { activeView } = useContext(WaxContext);
+  const [result, setResult] = useState('');
+  const [isSubmitted, setIsSubmitted] = useState(false);
+  const [isLoading, setIsLoading] = useState(false);
+  const AskAiContentTransformation = config.AskAiContentTransformation;
+  const inputRef = useRef(null);
+  const [isScrollable, setIsScrollable] = useState(false);
+  const resultDivRef = useRef(null);
+  const [isVisible, setIsVisible] = useState(false);
+
+  useLayoutEffect(() => {
+    const WaxSurface = activeView.dom.getBoundingClientRect();
+    const { selection } = activeView.state;
+    const { from, to } = selection;
+    const end = activeView.coordsAtPos(to);
+    const overLayComponent = document.getElementById('ai-overlay');
+    let overLayComponentCoords;
+    if (overLayComponent)
+      overLayComponentCoords = overLayComponent.getBoundingClientRect();
+    const top = end.top - WaxSurface.top + 20;
+    const left = end.left - WaxSurface.left - overLayComponentCoords.width / 2;
+    setPosition({ ...position, left, top });
+  }, [position.left]);
+
+  useEffect(() => {
+    if (resultDivRef.current) {
+      setIsScrollable(
+        resultDivRef.current.scrollHeight > resultDivRef.current.clientHeight,
+      );
+    }
+  }, [result]);
+
+  useEffect(() => {
+    // Add a delay of 2 seconds before showing the overlay
+    const timer = setTimeout(() => {
+      setIsVisible(true);
+    }, 1216);
+
+    return () => clearTimeout(timer);
+  }, []);
+
+  const tryAgain = () => {
+    // Reset the state to initial values
+    setIsSubmitted(false);
+    setResult('');
+
+    // Call the handleSubmit function again
+    handleSubmit(new Event('submit'));
+    // add underline
+  };
+
+  const handleInsertTextBelow = () => {
+    insertTextBelowSelection(activeView, result);
+  };
+
+  const handleSubmit = async () => {
+    setIsLoading(true);
+    const inputValue = inputRef.current.value;
+
+    // Get the highlighted text from the editor
+    const { from, to } = activeView.state.selection;
+    const highlightedText = activeView.state.doc.textBetween(from, to);
+
+    // Combine the user's input and the highlighted text
+    const combinedInput = `${inputValue}\n\nHighlighted Text: ${highlightedText}`;
+
+    try {
+      const response = await AskAiContentTransformation(combinedInput);
+      setResult(response);
+      setIsSubmitted(true);
+    } catch (error) {
+      setResult(error);
+      setIsSubmitted(true);
+    } finally {
+      setIsLoading(false);
+    }
+  };
+
+  const handleReplaceText = () => {
+    replaceSelectedText(activeView, result);
+  };
+
+  const discardResults = () => {
+    // Clear the input field
+    inputRef.current.value = '';
+
+    // Reset the state variables
+    setResult('');
+    setIsSubmitted(false);
+  };
+
+  const handleKeyDown = e => {
+    if (e.key === 'Enter') {
+      handleSubmit();
+    }
+  };
+
+  return (
+    <>
+      <AskAIForm
+        className={`fade-in ${isVisible ? 'show' : ''}`}
+        id="ai-overlay"
+      >
+        <AskAIFormInput
+          id="askAiInput"
+          onKeyPress={handleKeyDown}
+          placeholder="Find a better way to word this"
+          ref={inputRef}
+          type="text"
+        />
+        <SubmitButton onClick={handleSubmit}>
+          {isLoading ? <icons.loaderIco /> : <icons.submitIco />}
+        </SubmitButton>
+      </AskAIForm>
+      {isSubmitted && (
+        <>
+          <ResultDiv ref={resultDivRef} isScrollable={isScrollable}>
+            <ResultText>{result}</ResultText>
+          </ResultDiv>
+          <ActionSection>
+            <ActionButton onClick={handleReplaceText}>
+              <ActionText>
+                <icons.replaceIco /> Replace selected text
+              </ActionText>
+            </ActionButton>
+            <ActionButton onClick={handleInsertTextBelow}>
+              <ActionText>
+                <icons.insertIco /> Insert
+              </ActionText>
+            </ActionButton>
+            <ActionButton onClick={tryAgain}>
+              <ActionText>
+                <icons.tryAgain /> Try again
+              </ActionText>
+            </ActionButton>
+            <ActionButton onClick={discardResults}>
+              <ActionText color="#FF4E4E">
+                <icons.deleteIco /> Discard
+              </ActionText>
+            </ActionButton>
+          </ActionSection>
+        </>
+      )}
+    </>
+  );
+};
+
+export default AskAIOverlay;
diff --git a/wax-prosemirror-services/src/AiService/components/AskAiButton.js b/wax-prosemirror-services/src/AiService/components/AskAiButton.js
new file mode 100644
index 0000000000000000000000000000000000000000..dfdb32275eef54d4f7dbd107e3f2aa13bb861b4d
--- /dev/null
+++ b/wax-prosemirror-services/src/AiService/components/AskAiButton.js
@@ -0,0 +1,57 @@
+/* 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
new file mode 100644
index 0000000000000000000000000000000000000000..f41275dbc3b762acfa604ada3cfa1acf368579fe
--- /dev/null
+++ b/wax-prosemirror-services/src/AiService/components/AskAiComponent.js
@@ -0,0 +1,31 @@
+/* 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/plugins/AskAiSelectionPlugin.js b/wax-prosemirror-services/src/AiService/plugins/AskAiSelectionPlugin.js
new file mode 100644
index 0000000000000000000000000000000000000000..41672bdf6104e2361b0adbfcb3be9a20101f3799
--- /dev/null
+++ b/wax-prosemirror-services/src/AiService/plugins/AskAiSelectionPlugin.js
@@ -0,0 +1,45 @@
+import { Plugin, PluginKey } from 'prosemirror-state';
+import { Decoration, DecorationSet } from 'prosemirror-view';
+
+const key = new PluginKey('askAiSelectionPlugin');
+
+export default props => {
+  return new Plugin({
+    key,
+    state: {
+      init: (_, state) => {
+        return {};
+      },
+      apply(tr, prev, prevState, newState) {
+        let createDecoration;
+        const askAiInput = document.getElementById('askAiInput');
+
+        if (askAiInput) {
+          createDecoration = DecorationSet.create(newState.doc, [
+            Decoration.inline(newState.selection.from, newState.selection.to, {
+              class: 'ask-ai-selection',
+            }),
+          ]);
+        }
+
+        return {
+          createDecoration,
+        };
+      },
+    },
+    props: {
+      decorations: state => {
+        const askAiSelectionPluginState = state && key.getState(state);
+        return askAiSelectionPluginState.createDecoration;
+      },
+      //   handleDOMEvents: {
+      //     blur(view) {
+      //       view.dispatch(view.state.tr.setMeta(key, false));
+      //     },
+      //     focus(view) {
+      //       view.dispatch(view.state.tr.setMeta(key, true));
+      //     },
+      //   },
+    },
+  });
+};
diff --git a/wax-prosemirror-services/src/AiService/replaceText.js b/wax-prosemirror-services/src/AiService/replaceText.js
new file mode 100644
index 0000000000000000000000000000000000000000..ca63d257453870580ce524d99a31054f4f928233
--- /dev/null
+++ b/wax-prosemirror-services/src/AiService/replaceText.js
@@ -0,0 +1,97 @@
+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 } }));
+    },
+  );
+};