Skip to content

Commit

Permalink
feat: Switch textarea out for slate
Browse files Browse the repository at this point in the history
fixes #2

Signed-off-by: Colton Wolkins (Laptop) <[email protected]>
  • Loading branch information
TheTechmage committed May 29, 2024
1 parent d9fe493 commit 02e1a3c
Show file tree
Hide file tree
Showing 4 changed files with 533 additions and 14 deletions.
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"lint": "next lint"
},
"dependencies": {
"@emotion/css": "^11.11.2",
"@noble/curves": "^1.3.0",
"@types/dompurify": "^3.0.5",
"didcomm": "^0.4.1",
Expand All @@ -22,9 +23,13 @@
"multicodec": "^3.2.1",
"next": "14.1.0",
"next-usequerystate": "^1.16.0",
"react": "^18",
"react-dom": "^18",
"prismjs": "^1.29.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-textarea-autosize": "^8.5.3",
"slate": "^0.103.0",
"slate-history": "^0.100.0",
"slate-react": "^0.104.0",
"uuid": "^9.0.1"
},
"devDependencies": {
Expand Down
19 changes: 12 additions & 7 deletions src/app/wyvern/server-chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Button, TextInput } from 'flowbite-react';
import TextareaAutosize from 'react-textarea-autosize';
import { marked } from "marked";
import DOMPurify from 'dompurify'
import Editor from '@/components/editor'

function MessageElem({ message }) {
console.log(message)
Expand Down Expand Up @@ -41,10 +42,15 @@ function RichMessageElem({ message }) {
}

function Chatbox({ sendMessage, serverName, text, setText }) {
// <TextareaAutosize minRows="1" maxRows="6" className="pl-1 pr-1 grow" disabled={!serverName} value={text} onChange={(e) => setText(e.target.value)} />
return (
<form className="" onSubmit={sendMessage}>
<div className="h-12 bg-slate-500 flex items-center">
<TextareaAutosize minRows="1" maxRows="6" className="pl-1 pr-1 grow" disabled={!serverName} value={text} onChange={(e) => setText(e.target.value)} />
<form className="" onSubmit={event => {
event.preventDefault();
sendMessage(text)
setText("");
}}>
<div className="min-h-12 bg-slate-500 flex items-center">
<Editor onSubmit={sendMessage} />
<Button type="submit" gradientDuoTone="purpleToBlue">Send</Button>
</div>
</form>
Expand Down Expand Up @@ -86,17 +92,16 @@ export default function Chat({ serverId }) {
boundAgent = true;
}

function sendMessage(event) {
event.preventDefault();
function sendMessage(msg) {
const message = {
type: "https://didcomm.org/basicmessage/2.0/message",
lang: "en",
body: {
content: text,
content: msg,
},
}
setText("");
agent.sendMessage(serverId, message)
getMessages();
}

function scrollToBottom(ref) {
Expand Down
160 changes: 160 additions & 0 deletions src/components/editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import React, { useState, useCallback, useMemo } from 'react'
import { Node as SNode, Transforms, Text, createEditor, Descendant } from 'slate'

import { Slate, Editable, withReact } from 'slate-react'
import { withHistory } from 'slate-history'
import { css } from '@emotion/css'

import Prism from 'prismjs'
import 'prismjs/components/prism-markdown'

const Editor = ({ onSubmit }) => {
const [value, setValue] = useState(initialValue);

const renderLeaf = useCallback(props => <Leaf {...props} />, [])

const editor = useMemo(() => withHistory(withReact(createEditor())), []);

const decorate = useCallback(([node, path]) => {
const ranges = [];

if(!Text.isText(node)) {
return ranges;
}

const getLength = token => {
if(typeof token === 'string')
return token.length;
else if(typeof token.content == 'string')
return token.content.length;
else
return token.content.reduce((l, t) => l + getLength(t), 0);
}

const tokens = Prism.tokenize(node.text, Prism.languages.markdown);
let start = 0;

for(const token of tokens) {
const length = getLength(token);
const end = start + length;

if(typeof token !== 'string') {
ranges.push({
[token.type]: true,
anchor: {path, offset: start},
focus: {path, offset: end},
});
}

start = end;
}
return ranges;
}, []);

return (
<div className="grow">
<Slate
editor={editor}
initialValue={value}
onChange={(value) => {
setValue(value);
}}
>
<Editable
decorate={decorate}
renderLeaf={renderLeaf}
placeholder="Message for friends"
className="py-2 px-4 bg-slate-200 dark:bg-slate-600"
onKeyDown={event => {
if(!event.ctrlKey) {
return;
}
if(event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
onSubmit(value.map(n => SNode.string(n)).join('\n'));
const newValue = [{type: 'paragraph', children: [{text: ""}]}];
//const point = { path: [0, 0], offset: 0 }
//editor.selection = { anchor: point, focus: point };
//editor.history = { redos: [], undos: [] };
//editor.children = newValue;
Transforms.select(editor, { offset: 0, path: [0, 0] });
editor.children.map(item => Transforms.delete(editor, { at: [0] }));
editor.children = newValue;
editor.onChange();
//setValue(newValue);
}
}}
/>
</Slate>
</div>
);
};

const Leaf = ({ attributes, children, leaf }) => {
return (
<span
{...attributes}
className={css`
font-weight: ${leaf.bold && 'bold'};
font-style: ${leaf.italic && 'italic'};
text-decoration: ${leaf.underlined && 'underline'};
${leaf.title &&
css`
display: inline-block;
font-weight: bold;
font-size: 20px;
margin: 20px 0 10px 0;
`}
${leaf.list &&
css`
padding-left: 10px;
font-size: 20px;
line-height: 10px;
`}
${leaf.hr &&
css`
display: block;
text-align: center;
border-bottom: 2px solid #ddd;
`}
${leaf.blockquote &&
css`
display: inline-block;
border-left: 2px solid #ddd;
padding-left: 10px;
color: #aaa;
font-style: italic;
`}
${leaf.code &&
css`
font-family: monospace;
background-color: #eee;
padding: 3px;
`}
`}
>
{children}
</span>
);
};

const initialValue: Descendant[] = [
{
type: 'paragraph',
children: [
{
text: 'Slate is flexible enough to add **decorations** that can format text based on its content. For example, this editor has **Markdown** preview decorations on it, to make it _dead_ simple to make an editor with built-in Markdown previewing.',
},
],
},
{
type: 'paragraph',
children: [{ text: '## Try it out!' }],
},
{
type: 'paragraph',
children: [{ text: 'Try it out for yourself!' }],
},
]

export default Editor;
Loading

0 comments on commit 02e1a3c

Please sign in to comment.