Skip to content

Commit b0f8a4d

Browse files
committed
feat: syntax highlighting
1 parent 11f6bcf commit b0f8a4d

File tree

11 files changed

+875
-674
lines changed

11 files changed

+875
-674
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/**
2+
* Additional Prism.js overrides to match Monokai theme
3+
*/
4+
5+
/* Remove background and border radius from Prism defaults */
6+
pre[class*="language-"],
7+
code[class*="language-"] {
8+
text-shadow: none !important;
9+
background: transparent !important;
10+
border-radius: 0 !important;
11+
box-shadow: none !important;
12+
font-family: "JetBrains Mono", monospace !important;
13+
white-space: pre !important;
14+
tab-size: 2 !important;
15+
}
16+
17+
/* Set code block background to Monokai */
18+
.code-window-content {
19+
background-color: #272822 !important;
20+
}
21+
22+
/* Ensure proper spacing and layout */
23+
.code-window-content pre {
24+
margin: 0 !important;
25+
padding: 0 !important;
26+
text-align: left !important;
27+
white-space: pre !important;
28+
}
29+
30+
.code-window-content code {
31+
display: block !important;
32+
overflow-x: auto !important;
33+
padding: 0 !important;
34+
white-space: pre !important;
35+
word-spacing: normal !important;
36+
word-break: normal !important;
37+
word-wrap: normal !important;
38+
line-height: 1.5;
39+
}
40+
41+
/* Token color overrides */
42+
.token.comment,
43+
.token.prolog,
44+
.token.doctype,
45+
.token.cdata {
46+
color: #8292a2 !important;
47+
}
48+
49+
.token.punctuation {
50+
color: #f8f8f2 !important;
51+
}
52+
53+
.token.property,
54+
.token.tag,
55+
.token.constant,
56+
.token.symbol,
57+
.token.deleted {
58+
color: #f92672 !important;
59+
}
60+
61+
.token.boolean,
62+
.token.number {
63+
color: #ae81ff !important;
64+
}
65+
66+
.token.selector,
67+
.token.attr-name,
68+
.token.string,
69+
.token.char,
70+
.token.builtin,
71+
.token.inserted {
72+
color: #a6e22e !important;
73+
}
74+
75+
.token.operator,
76+
.token.entity,
77+
.token.url,
78+
.language-css .token.string,
79+
.style .token.string,
80+
.token.variable {
81+
color: #f8f8f2 !important;
82+
}
83+
84+
.token.atrule,
85+
.token.attr-value,
86+
.token.function {
87+
color: #e6db74 !important;
88+
}
89+
90+
.token.keyword {
91+
color: #66d9ef !important;
92+
}
93+
94+
.token.regex,
95+
.token.important {
96+
color: #fd971f !important;
97+
}
98+
99+
.token.important,
100+
.token.bold {
101+
font-weight: bold;
102+
}
103+
104+
.token.italic {
105+
font-style: italic;
106+
}
107+
108+
.token.entity {
109+
cursor: help;
110+
}

packages/docs/components/CodeBlock.vue

Lines changed: 197 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
<div class="code-window-dot bg-warning"></div>
1111
<div class="code-window-dot bg-success"></div>
1212
</div>
13-
<div class="language-label text-gray-500 text-xs">{{ language }}</div>
13+
<div class="language-label text-gray-500 text-xs">
14+
{{ language.toUpperCase() }}
15+
</div>
1416
<button
1517
v-if="code"
1618
@click="copyCode"
@@ -19,33 +21,59 @@
1921
{{ copied ? "Copied!" : "Copy" }}
2022
</button>
2123
</div>
22-
<div
23-
class="code-window-content relative p-6 bg-[#272822] text-white overflow-auto"
24-
>
25-
<!-- Code content with Monokai-like background -->
26-
<div class="relative z-10">
27-
<slot></slot>
28-
</div>
24+
<div class="code-window-content relative p-6 bg-[#272822] text-white">
25+
<ClientOnly>
26+
<!-- Prism-highlighted code (client-side only) -->
27+
<div class="flex">
28+
<!-- Line numbers -->
29+
<div
30+
v-if="showLineNumbers"
31+
class="line-numbers select-none pr-4 opacity-50 text-right"
32+
>
33+
<div
34+
v-for="n in lineCount"
35+
:key="n"
36+
class="text-gray-500 leading-relaxed"
37+
>
38+
{{ n }}
39+
</div>
40+
</div>
2941

30-
<!-- Line numbers -->
31-
<div
32-
v-if="showLineNumbers"
33-
class="absolute top-6 left-4 text-opacity-50 select-none"
34-
>
35-
<div
36-
v-for="n in lineCount"
37-
:key="n"
38-
class="text-gray-500 text-right pr-4"
39-
>
40-
{{ n }}
42+
<!-- Code content -->
43+
<pre
44+
class="flex-1 relative z-10 m-0 p-0 bg-transparent overflow-x-auto whitespace-pre"
45+
><code :class="`language-${prismLanguage}`" v-html="highlightedCode"></code></pre>
4146
</div>
42-
</div>
47+
<template #fallback>
48+
<!-- Non-highlighted fallback for SSR -->
49+
<div class="flex">
50+
<!-- Line numbers -->
51+
<div
52+
v-if="showLineNumbers"
53+
class="line-numbers select-none pr-4 opacity-50 text-right"
54+
>
55+
<div
56+
v-for="n in lineCount"
57+
:key="n"
58+
class="text-gray-500 leading-relaxed"
59+
>
60+
{{ n }}
61+
</div>
62+
</div>
63+
64+
<!-- Code content -->
65+
<pre
66+
class="flex-1 relative z-10 m-0 p-0 bg-transparent overflow-x-auto whitespace-pre"
67+
><code>{{ code }}</code></pre>
68+
</div>
69+
</template>
70+
</ClientOnly>
4371
</div>
4472
</div>
4573
</template>
4674

4775
<script setup>
48-
import { ref, computed, onMounted } from "vue"
76+
import { ref, computed, onMounted, watch } from "vue"
4977
5078
const props = defineProps({
5179
language: {
@@ -62,12 +90,77 @@ const props = defineProps({
6290
},
6391
})
6492
93+
// Map language aliases to Prism language names
94+
const languageMap = {
95+
js: "javascript",
96+
ts: "typescript",
97+
bash: "bash",
98+
shell: "bash",
99+
sh: "bash",
100+
html: "markup",
101+
xml: "markup",
102+
yml: "yaml",
103+
py: "python",
104+
}
105+
106+
const prismLanguage = computed(() => {
107+
return (
108+
languageMap[props.language.toLowerCase()] || props.language.toLowerCase()
109+
)
110+
})
111+
112+
const nuxtApp = useNuxtApp()
65113
const copied = ref(false)
114+
const highlightedCode = ref("")
115+
66116
const lineCount = computed(() => {
67117
if (!props.code) return 0
68118
return props.code.split("\n").length
69119
})
70120
121+
const highlightCode = () => {
122+
if (!process.client || !props.code) {
123+
highlightedCode.value = props.code
124+
return
125+
}
126+
127+
try {
128+
const Prism = nuxtApp.$prism
129+
if (!Prism) {
130+
console.warn("Prism.js not available")
131+
highlightedCode.value = props.code
132+
return
133+
}
134+
135+
// Use the mapped language
136+
const lang = prismLanguage.value
137+
138+
// Check if language is loaded
139+
if (Prism.languages[lang]) {
140+
highlightedCode.value = Prism.highlight(
141+
props.code,
142+
Prism.languages[lang],
143+
lang
144+
)
145+
} else {
146+
console.warn(`Language '${lang}' not loaded in Prism`)
147+
highlightedCode.value = props.code
148+
}
149+
} catch (error) {
150+
console.error("Error highlighting code:", error)
151+
highlightedCode.value = props.code
152+
}
153+
}
154+
155+
onMounted(() => {
156+
if (process.client) {
157+
highlightCode()
158+
}
159+
})
160+
161+
watch(() => props.code, highlightCode)
162+
watch(() => props.language, highlightCode)
163+
71164
const copyCode = () => {
72165
navigator.clipboard.writeText(props.code)
73166
copied.value = true
@@ -87,6 +180,88 @@ const copyCode = () => {
87180
88181
.code-window-dot {
89182
@apply w-2.5 h-2.5 rounded-full mr-1;
90-
/* No box-shadow */
183+
}
184+
185+
:deep(pre) {
186+
margin: 0;
187+
padding: 0;
188+
background: transparent !important;
189+
text-shadow: none !important;
190+
tab-size: 2;
191+
}
192+
193+
:deep(code) {
194+
font-family: "JetBrains Mono", monospace !important;
195+
font-size: 0.9rem !important;
196+
line-height: 1.5 !important;
197+
background: transparent !important;
198+
text-shadow: none !important;
199+
color: #f8f8f2 !important;
200+
white-space: pre !important;
201+
}
202+
203+
.line-numbers {
204+
min-width: 2rem;
205+
font-size: 0.9rem;
206+
line-height: 1.5;
207+
font-family: "JetBrains Mono", monospace;
208+
}
209+
210+
/* Override Prism.js token colors to match Monokai theme */
211+
:deep(.token.comment),
212+
:deep(.token.prolog),
213+
:deep(.token.doctype),
214+
:deep(.token.cdata) {
215+
color: #8292a2 !important;
216+
}
217+
218+
:deep(.token.punctuation) {
219+
color: #f8f8f2 !important;
220+
}
221+
222+
:deep(.token.property),
223+
:deep(.token.tag),
224+
:deep(.token.constant),
225+
:deep(.token.symbol),
226+
:deep(.token.deleted) {
227+
color: #f92672 !important;
228+
}
229+
230+
:deep(.token.boolean),
231+
:deep(.token.number) {
232+
color: #ae81ff !important;
233+
}
234+
235+
:deep(.token.selector),
236+
:deep(.token.attr-name),
237+
:deep(.token.string),
238+
:deep(.token.char),
239+
:deep(.token.builtin),
240+
:deep(.token.inserted) {
241+
color: #a6e22e !important;
242+
}
243+
244+
:deep(.token.operator),
245+
:deep(.token.entity),
246+
:deep(.token.url),
247+
:deep(.language-css .token.string),
248+
:deep(.style .token.string),
249+
:deep(.token.variable) {
250+
color: #f8f8f2 !important;
251+
}
252+
253+
:deep(.token.atrule),
254+
:deep(.token.attr-value),
255+
:deep(.token.function) {
256+
color: #e6db74 !important;
257+
}
258+
259+
:deep(.token.keyword) {
260+
color: #66d9ef !important;
261+
}
262+
263+
:deep(.token.regex),
264+
:deep(.token.important) {
265+
color: #fd971f !important;
91266
}
92267
</style>

0 commit comments

Comments
 (0)