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 = () => {
- + */}