Skip to content

Commit b09e02a

Browse files
feat(details): detect language for diffs
1 parent 8e44253 commit b09e02a

File tree

3 files changed

+126
-6
lines changed

3 files changed

+126
-6
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@shikijs/langs": "^3.4.2",
2525
"@shikijs/rehype": "^3.4.2",
2626
"@shikijs/themes": "^3.4.2",
27+
"@shikijs/transformers": "^3.5.0",
2728
"@sveltejs/adapter-vercel": "^5.7.2",
2829
"@sveltejs/kit": "^2.21.1",
2930
"@sveltejs/vite-plugin-svelte": "^5.0.3",

pnpm-lock.yaml

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/routes/[pid=pid]/[org]/[repo]/[id=number]/PageRenderer.svelte

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
MessagesSquare
3131
} from "@lucide/svelte";
3232
import rehypeShikiFromHighlighter from "@shikijs/rehype/core";
33+
import { transformerNotationDiff } from "@shikijs/transformers";
3334
import type { Plugin } from "svelte-exmarkdown";
3435
import type {
3536
DiscussionDetails,
@@ -60,19 +61,51 @@
6061
themes: { light: "github-light-default", dark: "github-dark-default" },
6162
transformers: [
6263
{
64+
preprocess(code, options) {
65+
if (options.lang === "diff") {
66+
const cleanedCode = code
67+
.split("\n")
68+
.map(line => line.replace(/^[+-]/, ""))
69+
.join("\n");
70+
const detectedLanguage = detectLanguage(cleanedCode);
71+
if (!detectedLanguage) return;
72+
options.lang = detectedLanguage;
73+
return code
74+
.split("\n")
75+
.map(line =>
76+
line.replace(
77+
/^([+-])(.*)/,
78+
(_, sign, rest) => `${rest} // [!code ${sign}${sign}]`
79+
)
80+
)
81+
.join("\n");
82+
}
83+
},
6384
pre(node) {
6485
node.properties["data-language"] = this.options.lang
6586
.toLowerCase()
6687
.replace(/^js$/, "javascript")
6788
.replace(/^ts$/, "typescript");
6889
}
69-
}
90+
},
91+
transformerNotationDiff()
7092
]
7193
} satisfies Parameters<typeof rehypeShikiFromHighlighter>[1]
7294
]
7395
};
7496
7597
// Utils
98+
function detectLanguage(code: string): string | undefined {
99+
const hasHTML = /<\/[a-zA-Z0-9-]+>/.test(code);
100+
const hasJS = / (let|var|const|=) /.test(code);
101+
102+
if (hasHTML && hasJS) return "svelte";
103+
if (hasHTML) return "html";
104+
if (hasJS) return /(: [A-Z]|type |interface )/.test(code) ? "ts" : "js";
105+
if (/[a-z-]+: \S+/.test(code)) return "css";
106+
return undefined;
107+
}
108+
76109
function formatToDateTime(date: string) {
77110
return new Intl.DateTimeFormat("en", {
78111
dateStyle: "medium",
@@ -584,16 +617,22 @@
584617
:global(html.dark .shiki),
585618
:global(html.dark .shiki span) {
586619
color: var(--shiki-dark) !important;
587-
background-color: var(--shiki-dark-bg) !important;
588620
font-style: var(--shiki-dark-font-style) !important;
589621
font-weight: var(--shiki-dark-font-weight) !important;
590622
text-decoration: var(--shiki-dark-text-decoration) !important;
591623
}
592624
625+
:global(html.dark .shiki),
626+
:global(html.dark .shiki .line:not(.diff) span) {
627+
background-color: var(--shiki-dark-bg) !important;
628+
}
629+
630+
/* Line numbers, credit to https://github.com/shikijs/shiki/issues/3#issuecomment-830564854 */
631+
/* Diff marks */
593632
:global {
594633
.shiki {
595-
/* Show line numbers */
596-
/* Credit to https://github.com/shikijs/shiki/issues/3#issuecomment-830564854 */
634+
position: relative;
635+
597636
code {
598637
counter-reset: step;
599638
counter-increment: step 0;
@@ -608,11 +647,62 @@
608647
white-space: nowrap;
609648
color: color-mix(in oklab, var(--color-muted-foreground) 70%, transparent);
610649
}
650+
651+
.diff {
652+
display: inline-block;
653+
transition: background-color 0.5s;
654+
655+
&::after {
656+
content: "";
657+
position: absolute;
658+
left: 0;
659+
width: 100%;
660+
height: 1lh;
661+
}
662+
663+
&.add {
664+
&::after {
665+
background-color: color-mix(
666+
in oklab,
667+
var(--color-green-500) 15%,
668+
transparent
669+
) !important;
670+
}
671+
672+
span:first-child::before {
673+
content: "+";
674+
color: var(--color-green-500);
675+
}
676+
}
677+
678+
&.remove {
679+
&::after {
680+
background-color: color-mix(
681+
in oklab,
682+
var(--color-destructive) 15%,
683+
transparent
684+
) !important;
685+
}
686+
687+
span {
688+
opacity: 0.7;
689+
}
690+
691+
span:first-child::before {
692+
content: "-";
693+
color: var(--color-destructive);
694+
}
695+
}
696+
697+
span:first-child::before {
698+
position: absolute;
699+
left: 2.75rem;
700+
}
701+
}
611702
}
612703
613-
/* Add language tag to code blocks */
704+
/* Language tag banner */
614705
&:is(pre[data-language]) {
615-
position: relative;
616706
padding-top: 3rem;
617707
border-radius: var(--radius-xl);
618708
border: 1px var(--tw-border-style) var(--color-border);

0 commit comments

Comments
 (0)