Skip to content

Commit 637785b

Browse files
committed
consistent code examples in CodePreview and markdown
1 parent 901c8f3 commit 637785b

2 files changed

Lines changed: 75 additions & 71 deletions

File tree

app/components/CodePreview.vue

Lines changed: 62 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const props = defineProps<{
1111
1212
const code = ref('')
1313
const highlightedCode = ref('')
14-
const copied = ref(false)
1514
1615
// Extract filename from path for display
1716
const filename = computed(() => {
@@ -22,112 +21,119 @@ const filename = computed(() => {
2221
const language = computed(() => {
2322
const extension = props.path.split('.').pop() || 'text'
2423
const languageMap: Record<string, string> = {
25-
'ts': 'typescript',
26-
'js': 'javascript',
27-
'vue': 'vue',
28-
'svelte': 'svelte', // Add this line
29-
'html': 'html',
30-
'css': 'css',
31-
'json': 'json',
32-
'md': 'markdown',
33-
'tsx': 'tsx',
34-
'jsx': 'jsx'
24+
ts: 'typescript',
25+
js: 'javascript',
26+
vue: 'vue',
27+
svelte: 'svelte', // Add this line
28+
html: 'html',
29+
css: 'css',
30+
json: 'json',
31+
md: 'markdown',
32+
tsx: 'tsx',
33+
jsx: 'jsx'
3534
}
3635
return languageMap[extension] || 'text'
3736
})
3837
3938
async function loadCodeAndComponent() {
4039
try {
4140
// Look in the app/.cache/examples directory
42-
const files = import.meta.glob([
43-
'../code/examples/**/*.ts',
44-
'../code/examples/**/*.tsx',
45-
'../code/examples/**/*.js',
46-
'../code/examples/**/*.jsx',
47-
'../code/examples/**/*.vue',
48-
'../code/examples/**/*.svelte',
49-
'../code/examples/**/*.md'
50-
], {
51-
as: 'raw',
52-
eager: false
53-
})
41+
const files = import.meta.glob(
42+
[
43+
'../code/examples/**/*.ts',
44+
'../code/examples/**/*.tsx',
45+
'../code/examples/**/*.js',
46+
'../code/examples/**/*.jsx',
47+
'../code/examples/**/*.vue',
48+
'../code/examples/**/*.svelte',
49+
'../code/examples/**/*.md'
50+
],
51+
{
52+
as: 'raw',
53+
eager: false
54+
}
55+
)
5456
5557
// Normalize the path by removing prefixes and cleaning up
5658
let normalizedPath = props.path
5759
.replace(/^@\/examples\//, '') // Remove @/examples/ prefix
5860
.replace(/^\.\.\/\.\.\/\.\.\/\.\.\/examples\//, '') // Remove ../../../../examples/ prefix
5961
.replace(/^examples\//, '') // Remove examples/ prefix if present
6062
.replace(/^\/*|\/*$/g, '') // Remove leading/trailing slashes
61-
63+
6264
// Try to find the file with the normalized path
63-
let fileKey = Object.keys(files).find(key =>
64-
key.includes(`/${normalizedPath}`)
65-
)
66-
65+
let fileKey = Object.keys(files).find((key) => key.includes(`/${normalizedPath}`))
66+
6767
// If not found, try to find it by just the filename
6868
if (!fileKey) {
6969
const filename = normalizedPath.split('/').pop()
70-
fileKey = Object.keys(files).find(key => key.endsWith(`/${filename}`))
70+
fileKey = Object.keys(files).find((key) => key.endsWith(`/${filename}`))
7171
}
72-
72+
7373
if (!fileKey) {
74-
throw new Error(`File not found: ${normalizedPath}. Make sure the file exists in the examples directory and has been copied to the cache.`)
74+
throw new Error(
75+
`File not found: ${normalizedPath}. Make sure the file exists in the examples directory and has been copied to the cache.`
76+
)
7577
}
7678
7779
const loadRaw = files[fileKey]
7880
if (!loadRaw) {
79-
throw new Error(`File not found: ${normalizedPath}. Make sure the file exists in the examples directory and has been copied to the cache.`)
81+
throw new Error(
82+
`File not found: ${normalizedPath}. Make sure the file exists in the examples directory and has been copied to the cache.`
83+
)
8084
}
8185
code.value = await loadRaw()
82-
86+
8387
highlightedCode.value = await codeToHtml(code.value, {
8488
lang: language.value,
85-
theme: 'github-dark',
89+
theme: 'github-dark'
8690
})
8791
} catch (error) {
8892
console.error(`Error loading file: ${props.path}`, error)
8993
code.value = `// Error loading file: ${props.path}\n// ${error instanceof Error ? error.message : String(error)}`
9094
highlightedCode.value = await codeToHtml(code.value, {
9195
lang: 'javascript',
92-
theme: 'github-dark',
96+
theme: 'github-dark'
9397
})
9498
}
9599
}
96100
97-
async function copyCode() {
98-
await navigator.clipboard.writeText(code.value)
99-
copied.value = true
100-
setTimeout(() => (copied.value = false), 1500)
101-
}
102-
103101
onMounted(loadCodeAndComponent)
104102
105-
watch(() => props.path, () => {
106-
loadCodeAndComponent()
107-
})
103+
watch(
104+
() => props.path,
105+
() => {
106+
loadCodeAndComponent()
107+
}
108+
)
108109
</script>
109110

110111
<template>
111-
<div class="code-preview my-6">
112-
<div class="relative">
113-
<Badge sm neutral class="absolute top-2 left-2 font-mono text-base-content/50">
114-
{{ filename }}
115-
</Badge>
116-
<Button xs neutral class="absolute top-2 right-4 cursor-pointer" @click="copyCode">
117-
{{ copied ? 'Copied!' : 'Copy' }}
118-
</Button>
119-
<pre class="overflow-x-auto rounded-lg p-4" v-html="highlightedCode" />
120-
</div>
121-
</div>
112+
<ProsePre :code="code" :filename="filename" class="code-preview">
113+
<div v-html="highlightedCode" />
114+
</ProsePre>
122115
</template>
123116

124117
<style>
118+
.code-preview > div > pre {
119+
padding-left: 0;
120+
}
121+
.code-preview {
122+
margin-top: 0;
123+
padding-top: 0;
124+
padding-bottom: 0;
125+
}
126+
.code-preview pre code span.line:not(:last-of-type):empty {
127+
display: block;
128+
margin-bottom: 1.5rem;
129+
}
125130
pre.shiki {
126131
background-color: var(--tw-prose-pre-bg) !important;
127132
color: var(--tw-prose-pre-code) !important;
133+
font-size: 16px;
128134
}
129135
pre.shiki code {
130136
display: flex;
131137
flex-direction: column;
132138
}
133-
</style>
139+
</style>

app/components/content/ProsePre.vue

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,29 @@ const copied = ref(false)
2121
2222
const copyToClipboard = async () => {
2323
if (!props.code) return
24-
24+
2525
try {
2626
await navigator.clipboard.writeText(props.code)
2727
copied.value = true
2828
setTimeout(() => {
2929
copied.value = false
30-
}, 2000)
30+
}, 1500)
3131
} catch (err) {
3232
console.error('Failed to copy text: ', err)
3333
}
3434
}
3535
</script>
3636

3737
<template>
38-
<span class="relative group">
39-
<pre :class="$props.class"><slot /></pre>
40-
<button
41-
v-if="code"
42-
@click="copyToClipboard"
43-
class="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200 bg-base-300 hover:bg-base-200 text-base-content p-2 rounded-md text-sm"
44-
:class="{ 'opacity-100': copied }"
45-
:title="copied ? 'Copied!' : 'Copy to clipboard'"
46-
>
47-
<Icon v-if="!copied" name="feather:copy" size="16" />
48-
<Icon v-else name="feather:check" size="16" />
49-
</button>
50-
</span>
38+
<div class="my-6">
39+
<div class="relative">
40+
<Badge v-if="filename" sm neutral class="absolute top-2.5 left-2 font-mono text-base-content/50">
41+
{{ filename }}
42+
</Badge>
43+
<Button v-if="code" xs neutral class="absolute top-2 right-4 cursor-pointer" @click="copyToClipboard">
44+
{{ copied ? 'Copied!' : 'Copy' }}
45+
</Button>
46+
<pre :class="['overflow-x-auto rounded-lg p-4', filename ? 'pt-12' : '', $props.class]"><slot /></pre>
47+
</div>
48+
</div>
5149
</template>

0 commit comments

Comments
 (0)