Merge pull request 'Highlight code blocks' (#645) from fernandoporazzi/snort:code-highlighter into main
Reviewed-on: #645
This commit is contained in:
commit
12723cf54b
@ -19,6 +19,7 @@
|
||||
"debug": "^4.3.4",
|
||||
"dexie": "^3.2.4",
|
||||
"emojilib": "^3.0.10",
|
||||
"highlight.js": "^11.8.0",
|
||||
"light-bolt11-decoder": "^2.1.0",
|
||||
"match-sorter": "^6.3.1",
|
||||
"qr-code-styling": "^1.6.0-rc.1",
|
||||
|
14
packages/app/src/Element/CodeBlock.css
Normal file
14
packages/app/src/Element/CodeBlock.css
Normal file
@ -0,0 +1,14 @@
|
||||
.codeblock {
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.codeblock pre {
|
||||
overflow: auto;
|
||||
line-height: 1.4;
|
||||
font-size: var(--font-size);
|
||||
}
|
||||
|
||||
.hljs {
|
||||
background: #f6f8fa;
|
||||
}
|
24
packages/app/src/Element/CodeBlock.tsx
Normal file
24
packages/app/src/Element/CodeBlock.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { useEffect } from "react";
|
||||
import "highlight.js/styles/github.css";
|
||||
import "./CodeBlock.css";
|
||||
|
||||
const CodeBlock = ({ content, language }: { content: string; language?: string }) => {
|
||||
useEffect(() => {
|
||||
const importHljs = async () => {
|
||||
const hljs = (await import("highlight.js")).default;
|
||||
hljs.highlightAll();
|
||||
};
|
||||
|
||||
importHljs();
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={`codeblock ${language && `language-${language}`}`} dir="auto">
|
||||
<pre>
|
||||
<code className={language && `language-${language}`}>{content.trim()}</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodeBlock;
|
@ -11,6 +11,7 @@ import { ProxyImg } from "./ProxyImg";
|
||||
import { SpotlightMediaModal } from "./Deck/SpotlightMedia";
|
||||
import HighlightedText from "./HighlightedText";
|
||||
import { useTextTransformer } from "Hooks/useTextTransformCache";
|
||||
import CodeBlock from "./CodeBlock";
|
||||
|
||||
export interface TextProps {
|
||||
id: string;
|
||||
@ -254,6 +255,9 @@ export default function Text({
|
||||
if (element.type === "custom_emoji") {
|
||||
chunks.push(<ProxyImg src={element.content} size={15} className="custom-emoji" />);
|
||||
}
|
||||
if (element.type === "code_block") {
|
||||
chunks.push(<CodeBlock content={element.content} language={element.language} />);
|
||||
}
|
||||
if (element.type === "text") {
|
||||
chunks.push(
|
||||
<div className="text-frag">
|
||||
|
@ -34,3 +34,8 @@ export const CashuRegex = /(cashuA[A-Za-z0-9_-]{0,10000}={0,3})/i;
|
||||
* Regex to match any npub/nevent/naddr/nprofile/note
|
||||
*/
|
||||
export const MentionNostrEntityRegex = /@n(pub|profile|event|ote|addr|)1[acdefghjklmnpqrstuvwxyz023456789]+/g;
|
||||
|
||||
/**
|
||||
* Regex to match markdown code content
|
||||
*/
|
||||
export const MarkdownCodeRegex = /(```.+?```)/gms;
|
||||
|
@ -1,13 +1,31 @@
|
||||
import { unwrap } from "@snort/shared";
|
||||
|
||||
import { CashuRegex, FileExtensionRegex, HashtagRegex, InvoiceRegex, MentionNostrEntityRegex } from "./const";
|
||||
import {
|
||||
CashuRegex,
|
||||
FileExtensionRegex,
|
||||
HashtagRegex,
|
||||
InvoiceRegex,
|
||||
MarkdownCodeRegex,
|
||||
MentionNostrEntityRegex,
|
||||
} from "./const";
|
||||
import { validateNostrLink } from "./nostr-link";
|
||||
import { splitByUrl } from "./utils";
|
||||
|
||||
export interface ParsedFragment {
|
||||
type: "text" | "link" | "mention" | "invoice" | "media" | "cashu" | "hashtag" | "custom_emoji" | "highlighted_text";
|
||||
type:
|
||||
| "text"
|
||||
| "link"
|
||||
| "mention"
|
||||
| "invoice"
|
||||
| "media"
|
||||
| "cashu"
|
||||
| "hashtag"
|
||||
| "custom_emoji"
|
||||
| "highlighted_text"
|
||||
| "code_block";
|
||||
content: string;
|
||||
mimeType?: string;
|
||||
language?: string;
|
||||
}
|
||||
|
||||
export type Fragment = string | ParsedFragment;
|
||||
@ -179,6 +197,31 @@ function extractCustomEmoji(fragments: Fragment[], tags: Array<Array<string>>) {
|
||||
.flat();
|
||||
}
|
||||
|
||||
function extractMarkdownCode(fragments: Fragment[]): (string | ParsedFragment)[] {
|
||||
return fragments
|
||||
.map(f => {
|
||||
if (typeof f === "string") {
|
||||
return f.split(MarkdownCodeRegex).map(i => {
|
||||
if (i.startsWith("```") && i.endsWith("```")) {
|
||||
const firstLineBreakIndex = i.indexOf("\n");
|
||||
const lastLineBreakIndex = i.lastIndexOf("\n");
|
||||
|
||||
return {
|
||||
type: "code_block",
|
||||
content: i.substring(firstLineBreakIndex, lastLineBreakIndex),
|
||||
language: i.substring(3, firstLineBreakIndex),
|
||||
} as ParsedFragment;
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return f;
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
|
||||
export function transformText(body: string, tags: Array<Array<string>>) {
|
||||
let fragments = extractLinks([body]);
|
||||
fragments = extractMentions(fragments);
|
||||
@ -186,6 +229,7 @@ export function transformText(body: string, tags: Array<Array<string>>) {
|
||||
fragments = extractInvoices(fragments);
|
||||
fragments = extractCashuTokens(fragments);
|
||||
fragments = extractCustomEmoji(fragments, tags);
|
||||
fragments = extractMarkdownCode(fragments);
|
||||
fragments = fragments
|
||||
.map(a => {
|
||||
if (typeof a === "string") {
|
||||
|
@ -2718,6 +2718,7 @@ __metadata:
|
||||
emojilib: ^3.0.10
|
||||
eslint: ^8.48.0
|
||||
eslint-webpack-plugin: ^4.0.1
|
||||
highlight.js: ^11.8.0
|
||||
html-webpack-plugin: ^5.5.1
|
||||
jest: ^29.5.0
|
||||
jest-environment-jsdom: ^29.5.0
|
||||
@ -7278,6 +7279,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"highlight.js@npm:^11.8.0":
|
||||
version: 11.8.0
|
||||
resolution: "highlight.js@npm:11.8.0"
|
||||
checksum: d2578a57aee7315946ff19379053fd0a28b127baabf7617ab1d28d62cdc4eaf3d75053569cb8479a5afdc7a68f1ba9a6c1d612d8ae399b4b9aa43093b4fb6831
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2":
|
||||
version: 3.3.2
|
||||
resolution: "hoist-non-react-statics@npm:3.3.2"
|
||||
|
Loading…
x
Reference in New Issue
Block a user