add code highlight with prismjs

This commit is contained in:
Ajay Bura 2023-07-28 15:19:38 +05:30
parent b85f208402
commit 9134578c7e
8 changed files with 200 additions and 7 deletions

28
package-lock.json generated
View file

@ -40,6 +40,7 @@
"linkifyjs": "4.0.2",
"matrix-js-sdk": "24.1.0",
"millify": "6.1.0",
"prismjs": "1.29.0",
"prop-types": "15.8.1",
"react": "17.0.2",
"react-autosize-textarea": "7.1.0",
@ -47,6 +48,7 @@
"react-dnd": "15.1.2",
"react-dnd-html5-backend": "15.1.3",
"react-dom": "17.0.2",
"react-error-boundary": "4.0.10",
"react-google-recaptcha": "2.1.0",
"react-modal": "3.16.1",
"sanitize-html": "2.8.0",
@ -62,6 +64,7 @@
"@rollup/plugin-inject": "5.0.3",
"@rollup/plugin-wasm": "6.1.1",
"@types/node": "18.11.18",
"@types/prismjs": "1.26.0",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.9",
"@types/sanitize-html": "2.9.0",
@ -1185,6 +1188,12 @@
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
"dev": true
},
"node_modules/@types/prismjs": {
"version": "1.26.0",
"resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz",
"integrity": "sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==",
"dev": true
},
"node_modules/@types/prop-types": {
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
@ -4816,6 +4825,14 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prismjs": {
"version": "1.29.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
"engines": {
"node": ">=6"
}
},
"node_modules/promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
@ -4960,6 +4977,17 @@
"react": "17.0.2"
}
},
"node_modules/react-error-boundary": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.10.tgz",
"integrity": "sha512-pvVKdi77j2OoPHo+p3rorgE43OjDWiqFkaqkJz8sJKK6uf/u8xtzuaVfj5qJ2JnDLIgF1De3zY5AJDijp+LVPA==",
"dependencies": {
"@babel/runtime": "^7.12.5"
},
"peerDependencies": {
"react": ">=16.13.1"
}
},
"node_modules/react-fast-compare": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",

View file

@ -50,6 +50,7 @@
"linkifyjs": "4.0.2",
"matrix-js-sdk": "24.1.0",
"millify": "6.1.0",
"prismjs": "1.29.0",
"prop-types": "15.8.1",
"react": "17.0.2",
"react-autosize-textarea": "7.1.0",
@ -57,6 +58,7 @@
"react-dnd": "15.1.2",
"react-dnd-html5-backend": "15.1.3",
"react-dom": "17.0.2",
"react-error-boundary": "4.0.10",
"react-google-recaptcha": "2.1.0",
"react-modal": "3.16.1",
"sanitize-html": "2.8.0",
@ -72,6 +74,7 @@
"@rollup/plugin-inject": "5.0.3",
"@rollup/plugin-wasm": "6.1.1",
"@types/node": "18.11.18",
"@types/prismjs": "1.26.0",
"@types/react": "18.0.26",
"@types/react-dom": "18.0.9",
"@types/sanitize-html": "2.9.0",

View file

@ -294,6 +294,7 @@ export function RoomTimeline({ room, eventId }: RoomTimelineProps) {
return (
<TooltipProvider
key={key}
position="Top"
tooltip={
<Tooltip style={{ maxWidth: toRem(200) }}>

View file

@ -1,5 +1,5 @@
/* eslint-disable jsx-a11y/alt-text */
import React from 'react';
import React, { Suspense, lazy } from 'react';
import {
Element,
Text as DOMText,
@ -12,9 +12,13 @@ import classNames from 'classnames';
import { Scroll, Text } from 'folds';
import { Opts as LinkifyOpts } from 'linkifyjs';
import Linkify from 'linkify-react';
import { ErrorBoundary } from 'react-error-boundary';
import * as css from '../styles/CustomHtml.css';
import { getMxIdLocalPart, getRoomWithCanonicalAlias } from '../utils/matrix';
import { getMemberDisplayName } from '../utils/room';
import { sanitizeText } from '../utils/sanitize';
const ReactPrism = lazy(() => import('./react-prism/ReactPrism'));
export const LINKIFY_OPTS: LinkifyOpts = {
attributes: {
@ -128,12 +132,33 @@ export const getReactCustomHtmlParser = (mx: MatrixClient, room: Room): HTMLReac
);
}
if (name === 'code' && !(parent && 'name' in parent && parent.name === 'pre')) {
return (
<code className={css.Code} {...props}>
{domToReact(children, opts)}
</code>
);
if (name === 'code') {
if (parent && 'name' in parent && parent.name === 'pre') {
const codeReact = domToReact(children, opts);
if (typeof codeReact === 'string') {
let lang = props.className;
if (lang === 'language-rs') lang = 'language-rust';
return (
<ErrorBoundary fallback={<code {...props}>{codeReact}</code>}>
<Suspense fallback={<code {...props}>{codeReact}</code>}>
<ReactPrism>
{(ref) => (
<code ref={ref} {...props} className={lang}>
{sanitizeText(codeReact)}
</code>
)}
</ReactPrism>
</Suspense>
</ErrorBoundary>
);
}
} else {
return (
<code className={css.Code} {...props}>
{domToReact(children, opts)}
</code>
);
}
}
if (name === 'a') {

View file

@ -0,0 +1,97 @@
.prism-light {
--prism-comment: #0f4777;
--prism-punctuation: #6d5050;
--prism-property: #9b1144;
--prism-boolean: #4816a3;
--prism-selector: #659604;
--prism-operator: #2a2a2a;
--prism-atrule: #7e6d00;
--prism-keyword: #00829f;
--prism-regex: #9b6426;
}
.prism-dark {
--prism-comment: #8292a2;
--prism-punctuation: #f8f8f2;
--prism-property: #f92672;
--prism-boolean: #ae81ff;
--prism-selector: #a6e22e;
--prism-operator: #f8f8f2;
--prism-atrule: #e6db74;
--prism-keyword: #66d9ef;
--prism-regex: #fd971f;
}
code .token.comment,
code .token.prolog,
code .token.doctype,
code .token.cdata {
color: var(--prism-comment);
}
code .token.punctuation {
color: var(--prism-punctuation);
}
code .token.namespace {
opacity: 0.7;
}
code .token.property,
code .token.tag,
code .token.constant,
code .token.symbol,
code .token.deleted {
color: var(--prism-property);
}
code .token.boolean,
code .token.number {
color: var(--prism-boolean);
}
code .token.selector,
code .token.attr-name,
code .token.string,
code .token.char,
code .token.builtin,
code .token.inserted {
color: var(--prism-selector);
}
code .token.operator,
code .token.entity,
code .token.url,
.language-css code .token.string,
.style code .token.string,
code .token.variable {
color: var(--prism-operator);
}
code .token.atrule,
code .token.attr-value,
code .token.function,
code .token.class-name {
color: var(--prism-atrule);
}
code .token.keyword {
color: var(--prism-keyword);
}
code .token.regex,
code .token.important {
color: var(--prism-regex);
}
code .token.important,
code .token.bold {
font-weight: bold;
}
code .token.italic {
font-style: italic;
}
code .token.entity {
cursor: help;
}

View file

@ -0,0 +1,35 @@
import React, { MutableRefObject, ReactNode, useEffect, useRef } from 'react';
import Prism from 'prismjs';
import 'prismjs/components/prism-json';
import 'prismjs/components/prism-javascript';
import 'prismjs/components/prism-typescript';
import 'prismjs/components/prism-css';
import 'prismjs/components/prism-sass';
import 'prismjs/components/prism-swift';
import 'prismjs/components/prism-rust';
import 'prismjs/components/prism-go';
import 'prismjs/components/prism-c';
import 'prismjs/components/prism-cpp';
import 'prismjs/components/prism-java';
import 'prismjs/components/prism-python';
import './ReactPrism.css';
// we apply theme in client/state/settings.js
// using classNames .prism-dark .prism-light from ReactPrism.css
export default function ReactPrism({
children,
}: {
children: (ref: MutableRefObject<null>) => ReactNode;
}) {
const codeRef = useRef<HTMLElement>(null);
useEffect(() => {
const el = codeRef.current;
if (el) Prism.highlightElement(el);
}, []);
return <>{children(codeRef as MutableRefObject<null>)}</>;
}

View file

@ -59,6 +59,8 @@ class Settings extends EventEmitter {
this.themes.forEach((themeName, index) => {
if (themeName !== '') document.body.classList.remove(themeName);
document.body.classList.remove(this.themeClasses[index]);
document.body.classList.remove('prism-light')
document.body.classList.remove('prism-dark')
});
}
@ -69,6 +71,7 @@ class Settings extends EventEmitter {
if (this.themes[themeIndex] === undefined) return
if (this.themes[themeIndex]) document.body.classList.add(this.themes[themeIndex]);
document.body.classList.add(this.themeClasses[themeIndex]);
document.body.classList.add(themeIndex < 2 ? 'prism-light' : 'prism-dark');
}
setTheme(themeIndex) {

View file

@ -3,6 +3,7 @@
"sourceMap": true,
"jsx": "react",
"target": "ES2016",
"module": "ES2020",
"allowJs": true,
"strict": true,
"esModuleInterop": true,