mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-02-13 17:10:06 +00:00
add code highlight with prismjs
This commit is contained in:
parent
b85f208402
commit
9134578c7e
28
package-lock.json
generated
28
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -294,6 +294,7 @@ export function RoomTimeline({ room, eventId }: RoomTimelineProps) {
|
|||
|
||||
return (
|
||||
<TooltipProvider
|
||||
key={key}
|
||||
position="Top"
|
||||
tooltip={
|
||||
<Tooltip style={{ maxWidth: toRem(200) }}>
|
||||
|
|
|
@ -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') {
|
||||
|
|
97
src/app/plugins/react-prism/ReactPrism.css
Normal file
97
src/app/plugins/react-prism/ReactPrism.css
Normal 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;
|
||||
}
|
35
src/app/plugins/react-prism/ReactPrism.tsx
Normal file
35
src/app/plugins/react-prism/ReactPrism.tsx
Normal 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>)}</>;
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"sourceMap": true,
|
||||
"jsx": "react",
|
||||
"target": "ES2016",
|
||||
"module": "ES2020",
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
|
|
Loading…
Reference in a new issue