From 9305e9f718b82496956bf3a15a75a18095c6695b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sat, 26 Apr 2025 18:27:32 +0400 Subject: [PATCH] Add code highlighting --- assets/css/layout.css | 5 + assets/css/prism.css | 140 ++++++++++++++++++++++++++++ assets/css/themes/default-light.css | 7 ++ config.ts | 9 ++ deno.lock | 3 +- html.ts | 1 + models/article.ts | 4 + prism.css | 140 ++++++++++++++++++++++++++++ 8 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 assets/css/prism.css create mode 100644 prism.css diff --git a/assets/css/layout.css b/assets/css/layout.css index 8d4e872..d245ddf 100644 --- a/assets/css/layout.css +++ b/assets/css/layout.css @@ -45,6 +45,11 @@ pre code { padding: 0.6rem 1rem; } +.highlight pre { + font-size: 0.9em; + padding: 0.6rem 1rem; +} + img { max-width: 100%; } diff --git a/assets/css/prism.css b/assets/css/prism.css new file mode 100644 index 0000000..5b8ed2d --- /dev/null +++ b/assets/css/prism.css @@ -0,0 +1,140 @@ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ + +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, pre[class*="language-"] ::selection, +code[class*="language-"]::selection, code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.token.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + /* This background color was intended by the author of this theme. */ + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/assets/css/themes/default-light.css b/assets/css/themes/default-light.css index 5e880b0..0c7d2a2 100644 --- a/assets/css/themes/default-light.css +++ b/assets/css/themes/default-light.css @@ -47,6 +47,13 @@ pre code { color: var(--text-color-dark-bg); } +.highlight pre, +.highlight code { + background-color: var(--background-color-body); + border: 2px solid #e8e3da; + color: var(--text-color-body); +} + dl dt { color: var(--text-color-discreet); } diff --git a/config.ts b/config.ts index b9036a4..0d1630e 100644 --- a/config.ts +++ b/config.ts @@ -52,6 +52,15 @@ const config = { }, query_timeout: parseInt(Deno.env.get("RELAY_TIMEOUT_MS") || "5000"), njump_url: Deno.env.get("NJUMP_URL") || "https://njump.me", + prism: { + // TODO make configurable via ENV + // Supported languages: https://app.unpkg.com/prismjs@1.29.0/files/components + extraLanguages: [ + "bash", + "markdown", + "typescript" + ] + } }; const staticUsersConfigured = Object.keys(staticUsers).length > 0; diff --git a/deno.lock b/deno.lock index 15a4f5a..4700bb2 100644 --- a/deno.lock +++ b/deno.lock @@ -54,6 +54,7 @@ "npm:nostr-tools@^2.7.0": "2.10.4", "npm:nostr-wasm@0.1.0": "0.1.0", "npm:path-to-regexp@6.2.1": "6.2.1", + "npm:prismjs@1.29.0": "1.29.0", "npm:prismjs@^1.29.0": "1.29.0", "npm:sanitize-html@^2.13.0": "2.13.1", "npm:websocket-ts@^2.1.5": "2.1.5", @@ -71,7 +72,7 @@ "npm:marked-alert", "npm:marked-footnote", "npm:marked-gfm-heading-id", - "npm:prismjs", + "npm:prismjs@^1.29.0", "npm:sanitize-html" ] }, diff --git a/html.ts b/html.ts index 5d1b96e..e70d555 100644 --- a/html.ts +++ b/html.ts @@ -19,6 +19,7 @@ function htmlLayout({ title, body, metaHtml }: HtmlLayoutOptions): string { ${title} ${metaHtml || ""} + diff --git a/models/article.ts b/models/article.ts index 8a32abc..4253ee0 100644 --- a/models/article.ts +++ b/models/article.ts @@ -4,6 +4,10 @@ import { NostrEvent as NEvent } from "@nostrify/nostrify"; import { replaceNostrUris } from "../nostr/links.ts"; import config from "../config.ts"; +for (const language of config.prism.extraLanguages) { + await import(`npm:prismjs@1.29.0/components/prism-${language}.js`); +} + export default class Article { event: NEvent; diff --git a/prism.css b/prism.css new file mode 100644 index 0000000..5b8ed2d --- /dev/null +++ b/prism.css @@ -0,0 +1,140 @@ +/** + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ + +code[class*="language-"], +pre[class*="language-"] { + color: black; + background: none; + text-shadow: 0 1px white; + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #b3d4fc; +} + +pre[class*="language-"]::selection, pre[class*="language-"] ::selection, +code[class*="language-"]::selection, code[class*="language-"] ::selection { + text-shadow: none; + background: #b3d4fc; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: .5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #f5f2f0; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: .1em; + border-radius: .3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.token.namespace { + opacity: .7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + /* This background color was intended by the author of this theme. */ + background: hsla(0, 0%, 100%, .5); +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #DD4A68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +}