enh: rich text input

This commit is contained in:
Timothy Jaeryang Baek 2024-11-20 22:56:26 -08:00
parent e30c5e628c
commit aca06f92e8
4 changed files with 138 additions and 1 deletions

58
package-lock.json generated
View File

@ -17,6 +17,7 @@
"@pyscript/core": "^0.4.32",
"@sveltejs/adapter-node": "^2.0.0",
"@tiptap/core": "^2.10.0",
"@tiptap/extension-code-block-lowlight": "^2.10.0",
"@tiptap/extension-highlight": "^2.10.0",
"@tiptap/extension-placeholder": "^2.10.0",
"@tiptap/extension-typography": "^2.10.0",
@ -2425,6 +2426,23 @@
"@tiptap/pm": "^2.7.0"
}
},
"node_modules/@tiptap/extension-code-block-lowlight": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.10.0.tgz",
"integrity": "sha512-dAv03XIHT5h+sdFmJzvx2FfpfFOOK9SBKHflRUdqTa8eA+0VZNAcPRjvJWVEWqts1fKZDJj774mO28NlhFzk9Q==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
"@tiptap/core": "^2.7.0",
"@tiptap/extension-code-block": "^2.7.0",
"@tiptap/pm": "^2.7.0",
"highlight.js": "^11",
"lowlight": "^2 || ^3"
}
},
"node_modules/@tiptap/extension-document": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.10.0.tgz",
@ -2793,6 +2811,16 @@
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
},
"node_modules/@types/hast": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/unist": "*"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@ -5162,6 +5190,20 @@
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz",
"integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw=="
},
"node_modules/devlop": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
"integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
"license": "MIT",
"peer": true,
"dependencies": {
"dequal": "^2.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/didyoumean": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
@ -7390,6 +7432,22 @@
"get-func-name": "^2.0.1"
}
},
"node_modules/lowlight": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.1.0.tgz",
"integrity": "sha512-CEbNVoSikAxwDMDPjXlqlFYiZLkDJHwyGu/MfOsJnF3d7f3tds5J3z8s/l9TMXhzfsJCCJEAsD78842mwmg0PQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/hast": "^3.0.0",
"devlop": "^1.0.0",
"highlight.js": "~11.9.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/magic-string": {
"version": "0.30.11",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",

View File

@ -58,6 +58,7 @@
"@pyscript/core": "^0.4.32",
"@sveltejs/adapter-node": "^2.0.0",
"@tiptap/core": "^2.10.0",
"@tiptap/extension-code-block-lowlight": "^2.10.0",
"@tiptap/extension-highlight": "^2.10.0",
"@tiptap/extension-placeholder": "^2.10.0",
"@tiptap/extension-typography": "^2.10.0",

View File

@ -230,3 +230,55 @@ input[type='number'] {
@apply dark:bg-gray-800 bg-gray-100;
}
/* Code styling */
.hljs-comment,
.hljs-quote {
color: #616161;
}
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #f98181;
}
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #fbbc88;
}
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #b9f18d;
}
.hljs-title,
.hljs-section {
color: #faf594;
}
.hljs-keyword,
.hljs-selector-tag {
color: #70cff8;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: 700;
}

View File

@ -11,13 +11,19 @@
import { Editor } from '@tiptap/core';
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight';
import Placeholder from '@tiptap/extension-placeholder';
import Highlight from '@tiptap/extension-highlight';
import Typography from '@tiptap/extension-typography';
import StarterKit from '@tiptap/starter-kit';
import { all, createLowlight } from 'lowlight';
import { PASTED_TEXT_CHARACTER_LIMIT } from '$lib/constants';
// create a lowlight instance with all languages loaded
const lowlight = createLowlight(all);
export let className = 'input-prose';
export let placeholder = 'Type here...';
export let value = '';
@ -109,7 +115,15 @@
onMount(() => {
editor = new Editor({
element: element,
extensions: [StarterKit, Highlight, Typography, Placeholder.configure({ placeholder })],
extensions: [
StarterKit,
CodeBlockLowlight.configure({
lowlight
}),
Highlight,
Typography,
Placeholder.configure({ placeholder })
],
content: marked.parse(value),
autofocus: true,
onTransaction: () => {
@ -144,10 +158,22 @@
}
if (messageInput) {
if (event.key === 'Enter') {
// Check if the current selection is inside a code block
const { state } = view;
const { $head } = state.selection;
const isInCodeBlock = $head.parent.type.name === 'codeBlock';
if (isInCodeBlock) {
return false; // Prevent Enter action inside a code block
}
}
// Handle shift + Enter for a line break
if (shiftEnter) {
if (event.key === 'Enter' && event.shiftKey) {
editor.commands.setHardBreak(); // Insert a hard break
view.dispatch(view.state.tr.scrollIntoView()); // Move viewport to the cursor
event.preventDefault();
return true;
}