forked from Kieran/snort
Merge pull request 'Highlight code blocks' (#645) from fernandoporazzi/snort:code-highlighter into main
Reviewed-on: Kieran/snort#645
This commit is contained in:
commit
12723cf54b
@ -19,6 +19,7 @@
|
|||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"dexie": "^3.2.4",
|
"dexie": "^3.2.4",
|
||||||
"emojilib": "^3.0.10",
|
"emojilib": "^3.0.10",
|
||||||
|
"highlight.js": "^11.8.0",
|
||||||
"light-bolt11-decoder": "^2.1.0",
|
"light-bolt11-decoder": "^2.1.0",
|
||||||
"match-sorter": "^6.3.1",
|
"match-sorter": "^6.3.1",
|
||||||
"qr-code-styling": "^1.6.0-rc.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 { SpotlightMediaModal } from "./Deck/SpotlightMedia";
|
||||||
import HighlightedText from "./HighlightedText";
|
import HighlightedText from "./HighlightedText";
|
||||||
import { useTextTransformer } from "Hooks/useTextTransformCache";
|
import { useTextTransformer } from "Hooks/useTextTransformCache";
|
||||||
|
import CodeBlock from "./CodeBlock";
|
||||||
|
|
||||||
export interface TextProps {
|
export interface TextProps {
|
||||||
id: string;
|
id: string;
|
||||||
@ -254,6 +255,9 @@ export default function Text({
|
|||||||
if (element.type === "custom_emoji") {
|
if (element.type === "custom_emoji") {
|
||||||
chunks.push(<ProxyImg src={element.content} size={15} className="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") {
|
if (element.type === "text") {
|
||||||
chunks.push(
|
chunks.push(
|
||||||
<div className="text-frag">
|
<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
|
* Regex to match any npub/nevent/naddr/nprofile/note
|
||||||
*/
|
*/
|
||||||
export const MentionNostrEntityRegex = /@n(pub|profile|event|ote|addr|)1[acdefghjklmnpqrstuvwxyz023456789]+/g;
|
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 { 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 { validateNostrLink } from "./nostr-link";
|
||||||
import { splitByUrl } from "./utils";
|
import { splitByUrl } from "./utils";
|
||||||
|
|
||||||
export interface ParsedFragment {
|
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;
|
content: string;
|
||||||
mimeType?: string;
|
mimeType?: string;
|
||||||
|
language?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Fragment = string | ParsedFragment;
|
export type Fragment = string | ParsedFragment;
|
||||||
@ -179,6 +197,31 @@ function extractCustomEmoji(fragments: Fragment[], tags: Array<Array<string>>) {
|
|||||||
.flat();
|
.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>>) {
|
export function transformText(body: string, tags: Array<Array<string>>) {
|
||||||
let fragments = extractLinks([body]);
|
let fragments = extractLinks([body]);
|
||||||
fragments = extractMentions(fragments);
|
fragments = extractMentions(fragments);
|
||||||
@ -186,6 +229,7 @@ export function transformText(body: string, tags: Array<Array<string>>) {
|
|||||||
fragments = extractInvoices(fragments);
|
fragments = extractInvoices(fragments);
|
||||||
fragments = extractCashuTokens(fragments);
|
fragments = extractCashuTokens(fragments);
|
||||||
fragments = extractCustomEmoji(fragments, tags);
|
fragments = extractCustomEmoji(fragments, tags);
|
||||||
|
fragments = extractMarkdownCode(fragments);
|
||||||
fragments = fragments
|
fragments = fragments
|
||||||
.map(a => {
|
.map(a => {
|
||||||
if (typeof a === "string") {
|
if (typeof a === "string") {
|
||||||
|
@ -2718,6 +2718,7 @@ __metadata:
|
|||||||
emojilib: ^3.0.10
|
emojilib: ^3.0.10
|
||||||
eslint: ^8.48.0
|
eslint: ^8.48.0
|
||||||
eslint-webpack-plugin: ^4.0.1
|
eslint-webpack-plugin: ^4.0.1
|
||||||
|
highlight.js: ^11.8.0
|
||||||
html-webpack-plugin: ^5.5.1
|
html-webpack-plugin: ^5.5.1
|
||||||
jest: ^29.5.0
|
jest: ^29.5.0
|
||||||
jest-environment-jsdom: ^29.5.0
|
jest-environment-jsdom: ^29.5.0
|
||||||
@ -7278,6 +7279,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2":
|
||||||
version: 3.3.2
|
version: 3.3.2
|
||||||
resolution: "hoist-non-react-statics@npm:3.3.2"
|
resolution: "hoist-non-react-statics@npm:3.3.2"
|
||||||
|
Loading…
Reference in New Issue
Block a user