diff --git a/bun.lock b/bun.lock
index 3f74829..819f89d 100644
--- a/bun.lock
+++ b/bun.lock
@@ -5,6 +5,7 @@
"": {
"dependencies": {
"dompurify": "^3.3.3",
+ "highlight.js": "^11.11.1",
"svelte": "^5.53.11",
},
"devDependencies": {
@@ -111,6 +112,8 @@
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
+ "highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
+
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
diff --git a/package.json b/package.json
index 0be7515..25b10d2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,7 @@
{
"dependencies": {
"dompurify": "^3.3.3",
+ "highlight.js": "^11.11.1",
"svelte": "^5.53.11"
},
"devDependencies": {
diff --git a/src/components/PlainTextWithCode.svelte b/src/components/PlainTextWithCode.svelte
index 7ef40f6..855922e 100644
--- a/src/components/PlainTextWithCode.svelte
+++ b/src/components/PlainTextWithCode.svelte
@@ -1,10 +1,16 @@
{#each segments as segment}
@@ -17,7 +23,13 @@
{:else if segment.kind == 'inlineCode'}
{segment.text}
{:else}
-
{segment.text}
+ {@const result = highlightCodeBlock(segment.text, segment.language)}
+
+ {#if result.kind == 'highlighted'}
+ {@html sanitizeHTML(result.html)}
+ {:else}
+ {result.text}
+ {/if}
{/if}
{/each}
@@ -47,6 +59,41 @@
white-space: pre;
}
+ pre code :global(.hljs-keyword),
+ pre code :global(.hljs-selector-tag),
+ pre code :global(.hljs-title.function_) {
+ color: #9d174d;
+ }
+
+ pre code :global(.hljs-built_in),
+ pre code :global(.hljs-title.class_),
+ pre code :global(.hljs-type) {
+ color: #7c3aed;
+ }
+
+ pre code :global(.hljs-string),
+ pre code :global(.hljs-attr),
+ pre code :global(.hljs-symbol) {
+ color: #047857;
+ }
+
+ pre code :global(.hljs-number),
+ pre code :global(.hljs-literal) {
+ color: #b45309;
+ }
+
+ pre code :global(.hljs-comment),
+ pre code :global(.hljs-quote) {
+ color: #6b7280;
+ font-style: italic;
+ }
+
+ pre code :global(.hljs-meta),
+ pre code :global(.hljs-tag),
+ pre code :global(.hljs-name) {
+ color: #0369a1;
+ }
+
@media (prefers-color-scheme: dark) {
code {
background-color: rgba(255, 255, 255, 0.12);
@@ -55,5 +102,39 @@
pre {
background-color: rgba(255, 255, 255, 0.1);
}
+
+ pre code :global(.hljs-keyword),
+ pre code :global(.hljs-selector-tag),
+ pre code :global(.hljs-title.function_) {
+ color: #f472b6;
+ }
+
+ pre code :global(.hljs-built_in),
+ pre code :global(.hljs-title.class_),
+ pre code :global(.hljs-type) {
+ color: #c4b5fd;
+ }
+
+ pre code :global(.hljs-string),
+ pre code :global(.hljs-attr),
+ pre code :global(.hljs-symbol) {
+ color: #6ee7b7;
+ }
+
+ pre code :global(.hljs-number),
+ pre code :global(.hljs-literal) {
+ color: #fbbf24;
+ }
+
+ pre code :global(.hljs-comment),
+ pre code :global(.hljs-quote) {
+ color: #9ca3af;
+ }
+
+ pre code :global(.hljs-meta),
+ pre code :global(.hljs-tag),
+ pre code :global(.hljs-name) {
+ color: #7dd3fc;
+ }
}
diff --git a/src/utils/code_highlighter.ts b/src/utils/code_highlighter.ts
new file mode 100644
index 0000000..2f1d77e
--- /dev/null
+++ b/src/utils/code_highlighter.ts
@@ -0,0 +1,82 @@
+import highlightJs from 'highlight.js/lib/core';
+
+import bash from 'highlight.js/lib/languages/bash';
+import c from 'highlight.js/lib/languages/c';
+import clojure from 'highlight.js/lib/languages/clojure';
+import cpp from 'highlight.js/lib/languages/cpp';
+import csharp from 'highlight.js/lib/languages/csharp';
+import css from 'highlight.js/lib/languages/css';
+import dart from 'highlight.js/lib/languages/dart';
+import elixir from 'highlight.js/lib/languages/elixir';
+import go from 'highlight.js/lib/languages/go';
+import java from 'highlight.js/lib/languages/java';
+import javascript from 'highlight.js/lib/languages/javascript';
+import json from 'highlight.js/lib/languages/json';
+import kotlin from 'highlight.js/lib/languages/kotlin';
+import markdown from 'highlight.js/lib/languages/markdown';
+import nix from 'highlight.js/lib/languages/nix';
+import objectivec from 'highlight.js/lib/languages/objectivec';
+import ocaml from 'highlight.js/lib/languages/ocaml';
+import perl from 'highlight.js/lib/languages/perl';
+import php from 'highlight.js/lib/languages/php';
+import powershell from 'highlight.js/lib/languages/powershell';
+import python from 'highlight.js/lib/languages/python';
+import r from 'highlight.js/lib/languages/r';
+import ruby from 'highlight.js/lib/languages/ruby';
+import rust from 'highlight.js/lib/languages/rust';
+import sql from 'highlight.js/lib/languages/sql';
+import swift from 'highlight.js/lib/languages/swift';
+import typescript from 'highlight.js/lib/languages/typescript';
+import xml from 'highlight.js/lib/languages/xml';
+import yaml from 'highlight.js/lib/languages/yaml';
+
+const languages = {
+ bash, c, clojure, cpp, csharp, css, dart, elixir, go, java, javascript, json, kotlin, markdown, nix,
+ objectivec, ocaml, perl, php, powershell, python, r, ruby, rust, sql, swift, typescript, xml, yaml
+};
+
+for (let [name, language] of Object.entries(languages)) {
+ highlightJs.registerLanguage(name, language);
+}
+
+const languageAliases: Record