csr tangled client in solid-js
15

Configure Feed

Select the types of activity you want to include in your feed.

add syntax highlighting on markdown code blocks

dawn (May 30, 2026, 1:49 AM +0300) 0a56ed4b fef752a1

+117 -72
+27 -1
src/components/repo.tsx
··· 43 43 breaks: false, 44 44 }); 45 45 46 + marked.use({ 47 + renderer: { 48 + code({ text, lang }) { 49 + const validLanguage = lang && hljs.getLanguage(lang) ? lang : 'plaintext'; 50 + const highlighted = hljs.highlight(text, { language: validLanguage }).value; 51 + return `<pre><code class="hljs language-${validLanguage}">${highlighted}</code></pre>`; 52 + }, 53 + }, 54 + }); 55 + 56 + DOMPurify.addHook('uponSanitizeAttribute', (_node, data) => { 57 + if (data.attrName === 'class') { 58 + const classes = data.attrValue.split(/\s+/); 59 + const filtered = classes.filter( 60 + (cls) => cls === 'hljs' || cls.startsWith('hljs-') || cls.startsWith('language-'), 61 + ); 62 + if (filtered.length > 0) { 63 + data.attrValue = filtered.join(' '); 64 + } else { 65 + return (data.keepAttr = false); 66 + } 67 + } 68 + }); 69 + 46 70 export const RepoTabLink: Component<{ 47 71 active: boolean; 48 72 href: string; ··· 876 900 877 901 export const MarkdownBlock: Component<{ markdown: string; variant?: 'default' | 'readme' | 'code' }> = (props) => { 878 902 const html = createMemo(() => 879 - DOMPurify.sanitize(marked.parse(props.markdown, { async: false }) as string), 903 + DOMPurify.sanitize(marked.parse(props.markdown, { async: false }) as string, { 904 + ADD_ATTR: ['class'], 905 + }), 880 906 ); 881 907 const classes = createMemo(() => 882 908 props.variant === 'readme'
+84 -68
src/index.css
··· 3 3 :root { 4 4 color-scheme: light dark; 5 5 font-family: InterVariable, system-ui, sans-serif, ui-sans-serif; 6 + 7 + --untangled-code-fg: #4c4f69; 8 + --untangled-code-muted: #8c8fa1; 9 + --untangled-code-keyword: #8839ef; 10 + --untangled-code-constant: #fe640b; 11 + --untangled-code-declaration: #d20f39; 12 + --untangled-code-namespace: #179299; 13 + --untangled-code-name: #1e66f5; 14 + --untangled-code-builtin: #04a5e5; 15 + --untangled-code-class: #df8e1d; 16 + --untangled-code-variable: #dc8a78; 17 + --untangled-code-string: #40a02b; 18 + --untangled-code-comment: #9ca0b0; 19 + --untangled-code-operator: #04a5e5; 20 + } 21 + 22 + @media (prefers-color-scheme: dark) { 23 + :root { 24 + --untangled-code-fg: #cad3f5; 25 + --untangled-code-muted: #8087a2; 26 + --untangled-code-keyword: #c6a0f6; 27 + --untangled-code-constant: #f5a97f; 28 + --untangled-code-declaration: #ed8796; 29 + --untangled-code-namespace: #8bd5ca; 30 + --untangled-code-name: #8aadf4; 31 + --untangled-code-builtin: #91d7e3; 32 + --untangled-code-class: #eed49f; 33 + --untangled-code-variable: #f4dbd6; 34 + --untangled-code-string: #a6da95; 35 + --untangled-code-comment: #6e738d; 36 + --untangled-code-operator: #91d7e3; 37 + } 6 38 } 7 39 8 40 .untangled-new-pr-step { ··· 2501 2533 } 2502 2534 2503 2535 .untangled-code-view { 2504 - --untangled-code-fg: #4c4f69; 2505 - --untangled-code-muted: #8c8fa1; 2506 - --untangled-code-keyword: #8839ef; 2507 - --untangled-code-constant: #fe640b; 2508 - --untangled-code-declaration: #d20f39; 2509 - --untangled-code-namespace: #179299; 2510 - --untangled-code-name: #1e66f5; 2511 - --untangled-code-builtin: #04a5e5; 2512 - --untangled-code-class: #df8e1d; 2513 - --untangled-code-variable: #dc8a78; 2514 - --untangled-code-string: #40a02b; 2515 - --untangled-code-comment: #9ca0b0; 2516 - --untangled-code-operator: #04a5e5; 2517 2536 display: table; 2518 2537 min-width: 100%; 2519 2538 font-family: IBMPlexMono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace; ··· 2529 2548 } 2530 2549 2531 2550 @media (prefers-color-scheme: dark) { 2532 - .untangled-code-view { 2533 - --untangled-code-fg: #cad3f5; 2534 - --untangled-code-muted: #8087a2; 2535 - --untangled-code-keyword: #c6a0f6; 2536 - --untangled-code-constant: #f5a97f; 2537 - --untangled-code-declaration: #ed8796; 2538 - --untangled-code-namespace: #8bd5ca; 2539 - --untangled-code-name: #8aadf4; 2540 - --untangled-code-builtin: #91d7e3; 2541 - --untangled-code-class: #eed49f; 2542 - --untangled-code-variable: #f4dbd6; 2543 - --untangled-code-string: #a6da95; 2544 - --untangled-code-comment: #6e738d; 2545 - --untangled-code-operator: #91d7e3; 2546 - } 2547 2551 } 2548 2552 2549 2553 .untangled-code-line { ··· 2596 2600 user-select: none; 2597 2601 } 2598 2602 2599 - .untangled-code-line-content .hljs-keyword, 2600 - .untangled-code-line-content .hljs-selector-tag, 2601 - .untangled-code-line-content .hljs-name, 2602 - .untangled-code-line-content .hljs-tag { 2603 + .prose :where(code):not(:where([class~="not-prose"], [class~="not-prose"] *))::before, 2604 + .prose :where(code):not(:where([class~="not-prose"], [class~="not-prose"] *))::after { 2605 + content: none; 2606 + } 2607 + 2608 + .prose pre code.hljs { 2609 + color: inherit; 2610 + background: transparent; 2611 + padding: 0; 2612 + font-weight: inherit; 2613 + } 2614 + 2615 + :where(.untangled-code-line-content, .hljs) .hljs-keyword, 2616 + :where(.untangled-code-line-content, .hljs) .hljs-selector-tag, 2617 + :where(.untangled-code-line-content, .hljs) .hljs-name, 2618 + :where(.untangled-code-line-content, .hljs) .hljs-tag { 2603 2619 color: var(--untangled-code-keyword); 2604 2620 } 2605 2621 2606 - .untangled-code-line-content .hljs-literal, 2607 - .untangled-code-line-content .hljs-number, 2608 - .untangled-code-line-content .hljs-symbol, 2609 - .untangled-code-line-content .hljs-bullet { 2622 + :where(.untangled-code-line-content, .hljs) .hljs-literal, 2623 + :where(.untangled-code-line-content, .hljs) .hljs-number, 2624 + :where(.untangled-code-line-content, .hljs) .hljs-symbol, 2625 + :where(.untangled-code-line-content, .hljs) .hljs-bullet { 2610 2626 color: var(--untangled-code-constant); 2611 2627 } 2612 2628 2613 - .untangled-code-line-content .hljs-built_in, 2614 - .untangled-code-line-content .hljs-builtin-name, 2615 - .untangled-code-line-content .hljs-type { 2629 + :where(.untangled-code-line-content, .hljs) .hljs-built_in, 2630 + :where(.untangled-code-line-content, .hljs) .hljs-builtin-name, 2631 + :where(.untangled-code-line-content, .hljs) .hljs-type { 2616 2632 color: var(--untangled-code-builtin); 2617 2633 } 2618 2634 2619 - .untangled-code-line-content .hljs-title, 2620 - .untangled-code-line-content .hljs-title.function_, 2621 - .untangled-code-line-content .hljs-function .hljs-title, 2622 - .untangled-code-line-content .hljs-selector-id { 2635 + :where(.untangled-code-line-content, .hljs) .hljs-title, 2636 + :where(.untangled-code-line-content, .hljs) .hljs-title.function_, 2637 + :where(.untangled-code-line-content, .hljs) .hljs-function .hljs-title, 2638 + :where(.untangled-code-line-content, .hljs) .hljs-selector-id { 2623 2639 color: var(--untangled-code-name); 2624 2640 } 2625 2641 2626 - .untangled-code-line-content .hljs-title.class_, 2627 - .untangled-code-line-content .hljs-class .hljs-title, 2628 - .untangled-code-line-content .hljs-section, 2629 - .untangled-code-line-content .hljs-selector-class { 2642 + :where(.untangled-code-line-content, .hljs) .hljs-title.class_, 2643 + :where(.untangled-code-line-content, .hljs) .hljs-class .hljs-title, 2644 + :where(.untangled-code-line-content, .hljs) .hljs-section, 2645 + :where(.untangled-code-line-content, .hljs) .hljs-selector-class { 2630 2646 color: var(--untangled-code-class); 2631 2647 } 2632 2648 2633 - .untangled-code-line-content .hljs-variable, 2634 - .untangled-code-line-content .hljs-template-variable, 2635 - .untangled-code-line-content .hljs-attr, 2636 - .untangled-code-line-content .hljs-attribute, 2637 - .untangled-code-line-content .hljs-property, 2638 - .untangled-code-line-content .hljs-params { 2649 + :where(.untangled-code-line-content, .hljs) .hljs-variable, 2650 + :where(.untangled-code-line-content, .hljs) .hljs-template-variable, 2651 + :where(.untangled-code-line-content, .hljs) .hljs-attr, 2652 + :where(.untangled-code-line-content, .hljs) .hljs-attribute, 2653 + :where(.untangled-code-line-content, .hljs) .hljs-property, 2654 + :where(.untangled-code-line-content, .hljs) .hljs-params { 2639 2655 color: var(--untangled-code-variable); 2640 2656 } 2641 2657 2642 - .untangled-code-line-content .hljs-string, 2643 - .untangled-code-line-content .hljs-regexp, 2644 - .untangled-code-line-content .hljs-template-tag, 2645 - .untangled-code-line-content .hljs-template-variable { 2658 + :where(.untangled-code-line-content, .hljs) .hljs-string, 2659 + :where(.untangled-code-line-content, .hljs) .hljs-regexp, 2660 + :where(.untangled-code-line-content, .hljs) .hljs-template-tag, 2661 + :where(.untangled-code-line-content, .hljs) .hljs-template-variable { 2646 2662 color: var(--untangled-code-string); 2647 2663 } 2648 2664 2649 - .untangled-code-line-content .hljs-comment, 2650 - .untangled-code-line-content .hljs-quote { 2665 + :where(.untangled-code-line-content, .hljs) .hljs-comment, 2666 + :where(.untangled-code-line-content, .hljs) .hljs-quote { 2651 2667 color: var(--untangled-code-comment); 2652 2668 font-style: italic; 2653 2669 } 2654 2670 2655 - .untangled-code-line-content .hljs-doctag, 2656 - .untangled-code-line-content .hljs-meta { 2671 + :where(.untangled-code-line-content, .hljs) .hljs-doctag, 2672 + :where(.untangled-code-line-content, .hljs) .hljs-meta { 2657 2673 color: var(--untangled-code-comment); 2658 2674 font-style: italic; 2659 2675 } 2660 2676 2661 - .untangled-code-line-content .hljs-operator, 2662 - .untangled-code-line-content .hljs-subst, 2663 - .untangled-code-line-content .hljs-link { 2677 + :where(.untangled-code-line-content, .hljs) .hljs-operator, 2678 + :where(.untangled-code-line-content, .hljs) .hljs-subst, 2679 + :where(.untangled-code-line-content, .hljs) .hljs-link { 2664 2680 color: var(--untangled-code-operator); 2665 2681 } 2666 2682 2667 - .untangled-code-line-content .hljs-deletion { 2683 + :where(.untangled-code-line-content, .hljs) .hljs-deletion { 2668 2684 color: var(--untangled-code-declaration); 2669 2685 } 2670 2686 2671 - .untangled-code-line-content .hljs-addition { 2687 + :where(.untangled-code-line-content, .hljs) .hljs-addition { 2672 2688 color: var(--untangled-code-string); 2673 2689 } 2674 2690 2675 - .untangled-code-line-content .hljs-emphasis { 2691 + :where(.untangled-code-line-content, .hljs) .hljs-emphasis { 2676 2692 font-style: italic; 2677 2693 } 2678 2694 2679 - .untangled-code-line-content .hljs-strong { 2695 + :where(.untangled-code-line-content, .hljs) .hljs-strong { 2680 2696 font-weight: 700; 2681 2697 } 2682 2698
+6 -3
src/layout.tsx
··· 1 1 import clsx from 'clsx'; 2 - import { Bell, BookMarked, LogOut, Plus, RotateCcw, Save, Search, Settings, UserRound } from 'lucide-solid'; 2 + import { Bell, BookMarked, LogOut, RotateCcw, Save, Search, Settings, UserRound } from 'lucide-solid'; 3 3 import { A, useLocation, useNavigate } from '@solidjs/router'; 4 4 import { createQuery, useQueryClient } from '@tanstack/solid-query'; 5 5 import { For, Show, createEffect, createSignal, onCleanup, onMount, type Component, type JSX } from 'solid-js'; ··· 230 230 <span class="hidden md:inline">{viewerQuery.data?.handle ?? auth.currentDid()}</span> 231 231 </summary> 232 232 <div class="untangled-topbar-menu-dropdown"> 233 - <button type="button" class="untangled-topbar-menu-item" onClick={() => void auth.signIn()}> 233 + {/* <button type="button" class="untangled-topbar-menu-item" onClick={() => { 234 + const id = prompt('Enter handle or DID'); 235 + if (id) void auth.signIn(id); 236 + }}> 234 237 <div class="untangled-topbar-menu-add-account-icon"> 235 238 <Plus class="size-4" /> 236 239 </div> 237 240 add account 238 - </button> 241 + </button> */} 239 242 240 243 <div class="untangled-topbar-menu-item-separator"> 241 244 <Show when={viewerQuery.data?.handle}>