diff --git a/.prettierignore b/.prettierignore index c6145fd8..1640ee12 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,6 @@ src/shared/translations lemmy-translations src/assets/css/themes/*.css +src/assets/css/code-themes/*.css stats.json dist diff --git a/package.json b/package.json index 95915c8f..0ec4c20a 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "markdown-it-container": "^3.0.0", "markdown-it-emoji": "^2.0.2", "markdown-it-footnote": "^3.0.3", + "markdown-it-highlightjs": "^4.0.1", "markdown-it-html5-embed": "^1.0.0", "markdown-it-ruby": "^0.1.1", "markdown-it-sub": "^1.0.0", diff --git a/src/assets/css/code-themes/atom-one-dark.css b/src/assets/css/code-themes/atom-one-dark.css new file mode 100644 index 00000000..5344ee38 --- /dev/null +++ b/src/assets/css/code-themes/atom-one-dark.css @@ -0,0 +1 @@ +pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#abb2bf;background:#282c34}.hljs-comment,.hljs-quote{color:#5c6370;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#c678dd}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e06c75}.hljs-literal{color:#56b6c2}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#98c379}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#d19a66}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#61aeee}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#e6c07b}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline} \ No newline at end of file diff --git a/src/assets/css/code-themes/atom-one-light.css b/src/assets/css/code-themes/atom-one-light.css new file mode 100644 index 00000000..df0268a9 --- /dev/null +++ b/src/assets/css/code-themes/atom-one-light.css @@ -0,0 +1 @@ +pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383a42;background:#fafafa}.hljs-comment,.hljs-quote{color:#a0a1a7;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#a626a4}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e45649}.hljs-literal{color:#0184bb}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#50a14f}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#986801}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#4078f2}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#c18401}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline} \ No newline at end of file diff --git a/src/server/handlers/code-theme-handler.ts b/src/server/handlers/code-theme-handler.ts new file mode 100644 index 00000000..37c9eda6 --- /dev/null +++ b/src/server/handlers/code-theme-handler.ts @@ -0,0 +1,35 @@ +import type { Request, Response } from "express"; +import { existsSync } from "fs"; +import path from "path"; + +const extraThemesFolder = + process.env["LEMMY_UI_EXTRA_THEMES_FOLDER"] || "./extra_themes"; + +export default async (req: Request, res: Response) => { + res.contentType("text/css"); + + const theme = req.params.name; + + if (!theme.endsWith(".css")) { + return res.status(400).send("Theme must be a css file"); + } + + const customTheme = path.resolve(extraThemesFolder, theme); + + if (existsSync(customTheme)) { + return res.sendFile(customTheme); + } else { + const internalTheme = path.resolve( + `./dist/assets/css/code-themes/${theme}`, + ); + + // If the theme doesn't exist, just send atom-one-light + if (existsSync(internalTheme)) { + return res.sendFile(internalTheme); + } else { + return res.sendFile( + path.resolve("./dist/assets/css/code-themes/atom-one-light.css"), + ); + } + } +}; diff --git a/src/server/index.tsx b/src/server/index.tsx index 1f47177b..f4216970 100644 --- a/src/server/index.tsx +++ b/src/server/index.tsx @@ -11,6 +11,7 @@ import ServiceWorkerHandler from "./handlers/service-worker-handler"; import ThemeHandler from "./handlers/theme-handler"; import ThemesListHandler from "./handlers/themes-list-handler"; import { setCacheControl, setDefaultCsp } from "./middleware"; +import CodeThemeHandler from "./handlers/code-theme-handler"; const server = express(); @@ -47,6 +48,7 @@ server.get("/robots.txt", RobotsHandler); server.get("/service-worker.js", ServiceWorkerHandler); server.get("/manifest.webmanifest", ManifestHandler); server.get("/css/themes/:name", ThemeHandler); +server.get("/css/code-themes/:name", CodeThemeHandler); server.get("/css/themelist", ThemesListHandler); server.get("/*", CatchAllHandler); diff --git a/src/shared/components/app/app.tsx b/src/shared/components/app/app.tsx index 323be486..c573ea41 100644 --- a/src/shared/components/app/app.tsx +++ b/src/shared/components/app/app.tsx @@ -15,6 +15,7 @@ import { Navbar } from "./navbar"; import "./styles.scss"; import { Theme } from "./theme"; import AnonymousGuard from "../common/anonymous-guard"; +import { CodeTheme } from "./code-theme"; interface AppProps { user?: MyUserInfo; @@ -55,7 +56,10 @@ export class App extends Component { {I18NextService.i18n.t("jump_to_content", "Jump to content")} {siteView && ( - + <> + + + )}
diff --git a/src/shared/components/app/code-theme.tsx b/src/shared/components/app/code-theme.tsx new file mode 100644 index 00000000..492fbefc --- /dev/null +++ b/src/shared/components/app/code-theme.tsx @@ -0,0 +1,22 @@ +import { Component } from "inferno"; + +export class CodeTheme extends Component { + render() { + return ( + <> + + + + ); + } +} diff --git a/src/shared/markdown.ts b/src/shared/markdown.ts index 38bf9d34..24a62a5e 100644 --- a/src/shared/markdown.ts +++ b/src/shared/markdown.ts @@ -14,6 +14,7 @@ import markdown_it_html5_embed from "markdown-it-html5-embed"; import markdown_it_ruby from "markdown-it-ruby"; import markdown_it_sub from "markdown-it-sub"; import markdown_it_sup from "markdown-it-sup"; +import markdown_it_highlightjs from "markdown-it-highlightjs"; import Renderer from "markdown-it/lib/renderer"; import Token from "markdown-it/lib/token"; import { instanceLinkRegex, relTags } from "./config"; @@ -170,6 +171,7 @@ export function setupMarkdown() { .use(markdown_it_footnote) .use(markdown_it_html5_embed, html5EmbedConfig) .use(markdown_it_container, "spoiler", spoilerConfig) + .use(markdown_it_highlightjs, { inline: true }) .use(markdown_it_ruby) .use(localInstanceLinkParser) .use(markdown_it_bidi); @@ -183,6 +185,7 @@ export function setupMarkdown() { .use(markdown_it_footnote) .use(markdown_it_html5_embed, html5EmbedConfig) .use(markdown_it_container, "spoiler", spoilerConfig) + .use(markdown_it_highlightjs, { inline: true }) .use(localInstanceLinkParser) .use(markdown_it_bidi) // .use(markdown_it_emoji, { diff --git a/yarn.lock b/yarn.lock index 1009595b..6e2b5c79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5020,6 +5020,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +highlight.js@^11.5.1: + version "11.9.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.9.0.tgz#04ab9ee43b52a41a047432c8103e2158a1b8b5b0" + integrity sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw== + history@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" @@ -6422,6 +6427,13 @@ markdown-it-footnote@^3.0.3: resolved "https://registry.yarnpkg.com/markdown-it-footnote/-/markdown-it-footnote-3.0.3.tgz#e0e4c0d67390a4c5f0c75f73be605c7c190ca4d8" integrity sha512-YZMSuCGVZAjzKMn+xqIco9d1cLGxbELHZ9do/TSYVzraooV8ypsppKNmUJ0fVH5ljkCInQAtFpm8Rb3eXSrt5w== +markdown-it-highlightjs@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/markdown-it-highlightjs/-/markdown-it-highlightjs-4.0.1.tgz#6b8eb6a3b971ed592db1ff160cfa5ce9f2e44232" + integrity sha512-EPXwFEN6P5nqR3G4KjT20r20xbGYKMMA/360hhSYFmeoGXTE6hsLtJAiB/8ID8slVH4CWHHEL7GX0YenyIstVQ== + dependencies: + highlight.js "^11.5.1" + markdown-it-html5-embed@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/markdown-it-html5-embed/-/markdown-it-html5-embed-1.0.0.tgz#f36bedca1eb12ce4df2d53b5ec72f62ba5e094b3"