add ritajs, let it show rhymes for the selected word upon clicking the rhyme FAB

This commit is contained in:
Silas 2023-12-21 10:14:49 -05:00
parent ad61063c0d
commit bb16921a74
Signed by: silentsilas
GPG Key ID: 4199EFB7DAA34349
6 changed files with 221 additions and 5 deletions

View File

@ -3,3 +3,14 @@
@import "tailwindcss/utilities";
/* This file is for your main application CSS */
#rhyme-fab {
background-color: #f9f9f9;
border: solid 1px #d4d4d4;
padding: 5px 10px;
border-radius: 25px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
}
#rhyme-fab:hover {
background-color: #e8e8e8;
}

View File

@ -1,6 +1,9 @@
// https://elixirforum.com/t/how-to-connect-quill-with-phoenix/46004
import Quill from 'quill';
import socket from '../user_socket';
import RhymeModule from '../lib/rhyme';
Quill.register('modules/rhymeModule', RhymeModule);
export let TextEditor = {
mounted() {
@ -8,7 +11,10 @@ export let TextEditor = {
this.clientId;
this.quill = new Quill(this.el, {
theme: 'snow'
theme: 'snow',
modules: {
rhymeModule: true
}
});
let channel = socket.channel(`pad:${padId}`, {});

90
assets/js/lib/rhyme.js Normal file
View File

@ -0,0 +1,90 @@
import { RiTa } from "rita";
class RhymeModule {
constructor(quill, options) {
this.rhymeElement = document.getElementById("poex-rhymes");
this.rhymeContent = "";
this.currentSelection = null;
this.fabElement = document.getElementById("rhyme-fab");
this.quill = quill;
this.quill.on("selection-change", (range) => this.onSelectionChange(range));
this.fabElement.addEventListener("click", () => {
this.onFabClick();
});
this.quill.root.addEventListener("contextmenu", (event) => {
event.preventDefault();
const range = this.quill.getSelection();
if (range && range.length > 0) {
const word = this.getWordAtCursor(range.index + range.length - 1);
if (word) {
this.findAndPrintRhymes(word).bind(this);
}
}
});
}
onTextChange() {
const range = this.quill.getSelection();
if (range && range.length === 0) {
const word = this.getWordAtCursor(range.index);
if (word) {
this.findAndPrintRhymes(word);
}
}
this.hideFab();
}
onSelectionChange(range) {
if (range && range.length > 0) {
const bounds = this.quill.getBounds(range);
this.currentSelection = this.quill.getSelection();
this.showFab(bounds.left + bounds.width / 2, bounds.top);
} else {
this.hideFab();
}
}
onFabClick() {
const range = this.currentSelection;
if (range && range.length > 0) {
const word = this.getWordAtCursor(range.index + range.length - 1);
if (word) {
this.findAndPrintRhymes(word);
}
}
}
showFab(x, y) {
this.fabElement.style.display = "block";
this.fabElement.style.position = "absolute";
this.fabElement.style.left = `${x}px`;
this.fabElement.style.top = `${y}px`;
}
hideFab() {
this.fabElement.style.display = "none";
}
getWordAtCursor(index) {
const text = this.quill.getText();
const wordBoundary = /\s/;
let start = index;
let end = index;
while (start > 0 && !wordBoundary.test(text[start - 1])) {
start--;
}
while (end < text.length && !wordBoundary.test(text[end])) {
end++;
}
return text.substring(start, end);
}
async findAndPrintRhymes(word) {
const rhymes = await RiTa.rhymes(word);
this.rhymeContent += `Rhymes for '${word}': ${rhymes.join(", ")}\n`;
this.rhymeElement.innerText = this.rhymeContent;
}
}
export default RhymeModule;

106
assets/package-lock.json generated
View File

@ -6,9 +6,49 @@
"": {
"dependencies": {
"quill": "^1.3.7",
"quill-delta": "^5.1.0"
"quill-delta": "^5.1.0",
"rita": "^3.0.23"
}
},
"node_modules/@chevrotain/cst-dts-gen": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz",
"integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==",
"dependencies": {
"@chevrotain/gast": "11.0.3",
"@chevrotain/types": "11.0.3",
"lodash-es": "4.17.21"
}
},
"node_modules/@chevrotain/gast": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz",
"integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==",
"dependencies": {
"@chevrotain/types": "11.0.3",
"lodash-es": "4.17.21"
}
},
"node_modules/@chevrotain/regexp-to-ast": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz",
"integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA=="
},
"node_modules/@chevrotain/types": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz",
"integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ=="
},
"node_modules/@chevrotain/utils": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz",
"integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ=="
},
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
},
"node_modules/call-bind": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
@ -22,6 +62,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/chevrotain": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz",
"integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
"dependencies": {
"@chevrotain/cst-dts-gen": "11.0.3",
"@chevrotain/gast": "11.0.3",
"@chevrotain/regexp-to-ast": "11.0.3",
"@chevrotain/types": "11.0.3",
"@chevrotain/utils": "11.0.3",
"lodash-es": "4.17.21"
}
},
"node_modules/clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
@ -192,6 +245,14 @@
"node": ">= 0.4"
}
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"bin": {
"he": "bin/he"
}
},
"node_modules/is-arguments": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
@ -236,6 +297,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
@ -246,6 +312,11 @@
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
},
"node_modules/mingo": {
"version": "6.4.10",
"resolved": "https://registry.npmjs.org/mingo/-/mingo-6.4.10.tgz",
"integrity": "sha512-/pOGeZKcZrKKw8YkCMKn9+XPiUYeNhkfaVbTn9tqvZvfccxf1idk8ezSulecZmPdKJLibNDtp4UBfDK3nzvMrQ=="
},
"node_modules/object-is": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
@ -334,6 +405,39 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/riscript": {
"version": "1.0.54",
"resolved": "https://registry.npmjs.org/riscript/-/riscript-1.0.54.tgz",
"integrity": "sha512-YNFbILV6NXsDr0EJizdQhE+fk+xrIRpJVsRjfxg/QqzB1MxhE08exOcQ5vqyZ24y4pqL/vie86t/G7NEV61BbQ==",
"dependencies": {
"chevrotain": "^11.0.3",
"he": "^1.2.0",
"mingo": "^6.4.8"
},
"engines": {
"node": ">=14.0"
},
"peerDependencies": {
"rita": "^3.0"
},
"peerDependenciesMeta": {
"rita": {
"optional": true
}
}
},
"node_modules/rita": {
"version": "3.0.23",
"resolved": "https://registry.npmjs.org/rita/-/rita-3.0.23.tgz",
"integrity": "sha512-EaYRNDqnqpnxxtdPI4AVTKricmvApZxg4GInMQZj8vYVfkfKOLWcvSd63rJ7g3syHxbMbOU3c6nEpFot8Uhafg==",
"dependencies": {
"@ungap/structured-clone": "^1.2.0",
"riscript": "^1.0.48"
},
"engines": {
"node": ">=14.0"
}
},
"node_modules/set-function-length": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",

View File

@ -1,6 +1,7 @@
{
"dependencies": {
"quill": "^1.3.7",
"quill-delta": "^5.1.0"
"quill-delta": "^5.1.0",
"rita": "^3.0.23"
}
}

View File

@ -1,5 +1,9 @@
<main class="container h-96 mx-auto py-12">
<div id="poex-text-editor" phx-hook="TextEditor" data-pad-id={@id} class="flex-auto">
<div id="rhyme-fab" style="display: none; position: absolute; z-index: 1000; cursor: pointer;">
<!-- You can use an icon or text here -->
<span>🔍 Rhymes</span>
</div>
<div id="poex-text-editor" phx-hook="TextEditor" data-pad-id={@id} class="flex-auto"></div>
<div id="poex-rhymes" class="flex-auto"></div>
</main>