diff --git a/src/components/repo.tsx b/src/components/repo.tsx index b974d38..b28fc32 100644 --- a/src/components/repo.tsx +++ b/src/components/repo.tsx @@ -43,6 +43,30 @@ marked.setOptions({ breaks: false, }); +marked.use({ + renderer: { + code({ text, lang }) { + const validLanguage = lang && hljs.getLanguage(lang) ? lang : 'plaintext'; + const highlighted = hljs.highlight(text, { language: validLanguage }).value; + return `
${highlighted}`;
+ },
+ },
+});
+
+DOMPurify.addHook('uponSanitizeAttribute', (_node, data) => {
+ if (data.attrName === 'class') {
+ const classes = data.attrValue.split(/\s+/);
+ const filtered = classes.filter(
+ (cls) => cls === 'hljs' || cls.startsWith('hljs-') || cls.startsWith('language-'),
+ );
+ if (filtered.length > 0) {
+ data.attrValue = filtered.join(' ');
+ } else {
+ return (data.keepAttr = false);
+ }
+ }
+});
+
export const RepoTabLink: Component<{
active: boolean;
href: string;
@@ -876,7 +900,9 @@ export const CommentCard: Component<{
export const MarkdownBlock: Component<{ markdown: string; variant?: 'default' | 'readme' | 'code' }> = (props) => {
const html = createMemo(() =>
- DOMPurify.sanitize(marked.parse(props.markdown, { async: false }) as string),
+ DOMPurify.sanitize(marked.parse(props.markdown, { async: false }) as string, {
+ ADD_ATTR: ['class'],
+ }),
);
const classes = createMemo(() =>
props.variant === 'readme'
diff --git a/src/index.css b/src/index.css
index eca6e28..981e3e6 100644
--- a/src/index.css
+++ b/src/index.css
@@ -3,6 +3,38 @@
:root {
color-scheme: light dark;
font-family: InterVariable, system-ui, sans-serif, ui-sans-serif;
+
+ --untangled-code-fg: #4c4f69;
+ --untangled-code-muted: #8c8fa1;
+ --untangled-code-keyword: #8839ef;
+ --untangled-code-constant: #fe640b;
+ --untangled-code-declaration: #d20f39;
+ --untangled-code-namespace: #179299;
+ --untangled-code-name: #1e66f5;
+ --untangled-code-builtin: #04a5e5;
+ --untangled-code-class: #df8e1d;
+ --untangled-code-variable: #dc8a78;
+ --untangled-code-string: #40a02b;
+ --untangled-code-comment: #9ca0b0;
+ --untangled-code-operator: #04a5e5;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --untangled-code-fg: #cad3f5;
+ --untangled-code-muted: #8087a2;
+ --untangled-code-keyword: #c6a0f6;
+ --untangled-code-constant: #f5a97f;
+ --untangled-code-declaration: #ed8796;
+ --untangled-code-namespace: #8bd5ca;
+ --untangled-code-name: #8aadf4;
+ --untangled-code-builtin: #91d7e3;
+ --untangled-code-class: #eed49f;
+ --untangled-code-variable: #f4dbd6;
+ --untangled-code-string: #a6da95;
+ --untangled-code-comment: #6e738d;
+ --untangled-code-operator: #91d7e3;
+ }
}
.untangled-new-pr-step {
@@ -2501,19 +2533,6 @@ details[open] > .untangled-language-bar {
}
.untangled-code-view {
- --untangled-code-fg: #4c4f69;
- --untangled-code-muted: #8c8fa1;
- --untangled-code-keyword: #8839ef;
- --untangled-code-constant: #fe640b;
- --untangled-code-declaration: #d20f39;
- --untangled-code-namespace: #179299;
- --untangled-code-name: #1e66f5;
- --untangled-code-builtin: #04a5e5;
- --untangled-code-class: #df8e1d;
- --untangled-code-variable: #dc8a78;
- --untangled-code-string: #40a02b;
- --untangled-code-comment: #9ca0b0;
- --untangled-code-operator: #04a5e5;
display: table;
min-width: 100%;
font-family: IBMPlexMono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
@@ -2529,21 +2548,6 @@ details[open] > .untangled-language-bar {
}
@media (prefers-color-scheme: dark) {
- .untangled-code-view {
- --untangled-code-fg: #cad3f5;
- --untangled-code-muted: #8087a2;
- --untangled-code-keyword: #c6a0f6;
- --untangled-code-constant: #f5a97f;
- --untangled-code-declaration: #ed8796;
- --untangled-code-namespace: #8bd5ca;
- --untangled-code-name: #8aadf4;
- --untangled-code-builtin: #91d7e3;
- --untangled-code-class: #eed49f;
- --untangled-code-variable: #f4dbd6;
- --untangled-code-string: #a6da95;
- --untangled-code-comment: #6e738d;
- --untangled-code-operator: #91d7e3;
- }
}
.untangled-code-line {
@@ -2596,87 +2600,99 @@ details[open] > .untangled-language-bar {
user-select: none;
}
-.untangled-code-line-content .hljs-keyword,
-.untangled-code-line-content .hljs-selector-tag,
-.untangled-code-line-content .hljs-name,
-.untangled-code-line-content .hljs-tag {
+.prose :where(code):not(:where([class~="not-prose"], [class~="not-prose"] *))::before,
+.prose :where(code):not(:where([class~="not-prose"], [class~="not-prose"] *))::after {
+ content: none;
+}
+
+.prose pre code.hljs {
+ color: inherit;
+ background: transparent;
+ padding: 0;
+ font-weight: inherit;
+}
+
+:where(.untangled-code-line-content, .hljs) .hljs-keyword,
+:where(.untangled-code-line-content, .hljs) .hljs-selector-tag,
+:where(.untangled-code-line-content, .hljs) .hljs-name,
+:where(.untangled-code-line-content, .hljs) .hljs-tag {
color: var(--untangled-code-keyword);
}
-.untangled-code-line-content .hljs-literal,
-.untangled-code-line-content .hljs-number,
-.untangled-code-line-content .hljs-symbol,
-.untangled-code-line-content .hljs-bullet {
+:where(.untangled-code-line-content, .hljs) .hljs-literal,
+:where(.untangled-code-line-content, .hljs) .hljs-number,
+:where(.untangled-code-line-content, .hljs) .hljs-symbol,
+:where(.untangled-code-line-content, .hljs) .hljs-bullet {
color: var(--untangled-code-constant);
}
-.untangled-code-line-content .hljs-built_in,
-.untangled-code-line-content .hljs-builtin-name,
-.untangled-code-line-content .hljs-type {
+:where(.untangled-code-line-content, .hljs) .hljs-built_in,
+:where(.untangled-code-line-content, .hljs) .hljs-builtin-name,
+:where(.untangled-code-line-content, .hljs) .hljs-type {
color: var(--untangled-code-builtin);
}
-.untangled-code-line-content .hljs-title,
-.untangled-code-line-content .hljs-title.function_,
-.untangled-code-line-content .hljs-function .hljs-title,
-.untangled-code-line-content .hljs-selector-id {
+:where(.untangled-code-line-content, .hljs) .hljs-title,
+:where(.untangled-code-line-content, .hljs) .hljs-title.function_,
+:where(.untangled-code-line-content, .hljs) .hljs-function .hljs-title,
+:where(.untangled-code-line-content, .hljs) .hljs-selector-id {
color: var(--untangled-code-name);
}
-.untangled-code-line-content .hljs-title.class_,
-.untangled-code-line-content .hljs-class .hljs-title,
-.untangled-code-line-content .hljs-section,
-.untangled-code-line-content .hljs-selector-class {
+:where(.untangled-code-line-content, .hljs) .hljs-title.class_,
+:where(.untangled-code-line-content, .hljs) .hljs-class .hljs-title,
+:where(.untangled-code-line-content, .hljs) .hljs-section,
+:where(.untangled-code-line-content, .hljs) .hljs-selector-class {
color: var(--untangled-code-class);
}
-.untangled-code-line-content .hljs-variable,
-.untangled-code-line-content .hljs-template-variable,
-.untangled-code-line-content .hljs-attr,
-.untangled-code-line-content .hljs-attribute,
-.untangled-code-line-content .hljs-property,
-.untangled-code-line-content .hljs-params {
+:where(.untangled-code-line-content, .hljs) .hljs-variable,
+:where(.untangled-code-line-content, .hljs) .hljs-template-variable,
+:where(.untangled-code-line-content, .hljs) .hljs-attr,
+:where(.untangled-code-line-content, .hljs) .hljs-attribute,
+:where(.untangled-code-line-content, .hljs) .hljs-property,
+:where(.untangled-code-line-content, .hljs) .hljs-params {
color: var(--untangled-code-variable);
}
-.untangled-code-line-content .hljs-string,
-.untangled-code-line-content .hljs-regexp,
-.untangled-code-line-content .hljs-template-tag,
-.untangled-code-line-content .hljs-template-variable {
+:where(.untangled-code-line-content, .hljs) .hljs-string,
+:where(.untangled-code-line-content, .hljs) .hljs-regexp,
+:where(.untangled-code-line-content, .hljs) .hljs-template-tag,
+:where(.untangled-code-line-content, .hljs) .hljs-template-variable {
color: var(--untangled-code-string);
}
-.untangled-code-line-content .hljs-comment,
-.untangled-code-line-content .hljs-quote {
+:where(.untangled-code-line-content, .hljs) .hljs-comment,
+:where(.untangled-code-line-content, .hljs) .hljs-quote {
color: var(--untangled-code-comment);
font-style: italic;
}
-.untangled-code-line-content .hljs-doctag,
-.untangled-code-line-content .hljs-meta {
+:where(.untangled-code-line-content, .hljs) .hljs-doctag,
+:where(.untangled-code-line-content, .hljs) .hljs-meta {
color: var(--untangled-code-comment);
font-style: italic;
}
-.untangled-code-line-content .hljs-operator,
-.untangled-code-line-content .hljs-subst,
-.untangled-code-line-content .hljs-link {
+:where(.untangled-code-line-content, .hljs) .hljs-operator,
+:where(.untangled-code-line-content, .hljs) .hljs-subst,
+:where(.untangled-code-line-content, .hljs) .hljs-link {
color: var(--untangled-code-operator);
}
-.untangled-code-line-content .hljs-deletion {
+:where(.untangled-code-line-content, .hljs) .hljs-deletion {
color: var(--untangled-code-declaration);
}
-.untangled-code-line-content .hljs-addition {
+:where(.untangled-code-line-content, .hljs) .hljs-addition {
color: var(--untangled-code-string);
}
-.untangled-code-line-content .hljs-emphasis {
+:where(.untangled-code-line-content, .hljs) .hljs-emphasis {
font-style: italic;
}
-.untangled-code-line-content .hljs-strong {
+:where(.untangled-code-line-content, .hljs) .hljs-strong {
font-weight: 700;
}
diff --git a/src/layout.tsx b/src/layout.tsx
index a78dc49..08b871e 100644
--- a/src/layout.tsx
+++ b/src/layout.tsx
@@ -1,5 +1,5 @@
import clsx from 'clsx';
-import { Bell, BookMarked, LogOut, Plus, RotateCcw, Save, Search, Settings, UserRound } from 'lucide-solid';
+import { Bell, BookMarked, LogOut, RotateCcw, Save, Search, Settings, UserRound } from 'lucide-solid';
import { A, useLocation, useNavigate } from '@solidjs/router';
import { createQuery, useQueryClient } from '@tanstack/solid-query';
import { For, Show, createEffect, createSignal, onCleanup, onMount, type Component, type JSX } from 'solid-js';
@@ -230,12 +230,15 @@ const UserProfileMenu: Component = () => {
{viewerQuery.data?.handle ?? auth.currentDid()}