From bb16921a746af787c626a4e44fbeac89b22cb73a Mon Sep 17 00:00:00 2001 From: Silas Date: Thu, 21 Dec 2023 10:14:49 -0500 Subject: [PATCH] add ritajs, let it show rhymes for the selected word upon clicking the rhyme FAB --- assets/css/app.css | 11 +++ assets/js/hooks/textEditHook.js | 8 +- assets/js/lib/rhyme.js | 90 +++++++++++++++++++ assets/package-lock.json | 106 ++++++++++++++++++++++- assets/package.json | 3 +- lib/poex_web/live/pad/pad_live.html.heex | 8 +- 6 files changed, 221 insertions(+), 5 deletions(-) create mode 100644 assets/js/lib/rhyme.js diff --git a/assets/css/app.css b/assets/css/app.css index 378c8f9..764c22e 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -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; +} diff --git a/assets/js/hooks/textEditHook.js b/assets/js/hooks/textEditHook.js index 5819e96..7166ea7 100644 --- a/assets/js/hooks/textEditHook.js +++ b/assets/js/hooks/textEditHook.js @@ -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}`, {}); diff --git a/assets/js/lib/rhyme.js b/assets/js/lib/rhyme.js new file mode 100644 index 0000000..4701b44 --- /dev/null +++ b/assets/js/lib/rhyme.js @@ -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; diff --git a/assets/package-lock.json b/assets/package-lock.json index d41847a..88e913c 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -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", diff --git a/assets/package.json b/assets/package.json index af26a45..96edc0f 100644 --- a/assets/package.json +++ b/assets/package.json @@ -1,6 +1,7 @@ { "dependencies": { "quill": "^1.3.7", - "quill-delta": "^5.1.0" + "quill-delta": "^5.1.0", + "rita": "^3.0.23" } } diff --git a/lib/poex_web/live/pad/pad_live.html.heex b/lib/poex_web/live/pad/pad_live.html.heex index 55c8d70..c72f47e 100644 --- a/lib/poex_web/live/pad/pad_live.html.heex +++ b/lib/poex_web/live/pad/pad_live.html.heex @@ -1,5 +1,9 @@
-
+ -
+
+