Skip to content

Commit ff7e392

Browse files
authored
Merge pull request #3 from cwandev/feature/code-block
feat: add code-block component
2 parents de7f3c6 + 96e8a50 commit ff7e392

File tree

19 files changed

+2022
-1294
lines changed

19 files changed

+2022
-1294
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ AI Elements Vue includes the following components:
8888
| `prompt-input` | ✅ 已完成 | Advanced input component with model selection |
8989
| `actions` | ✅ 已完成 | Interactive action buttons for AI responses |
9090
| `branch` | ✅ 已完成 | Branch visualization for conversation flows |
91-
| `code-block` | ❌ 未完成 | Syntax-highlighted code display with copy functionality |
91+
| `code-block` | ✅ 已完成 | Syntax-highlighted code display with copy functionality |
9292
| `image` | ❌ 未完成 | AI-generated image display component |
9393
| `inline-citation` | ❌ 未完成 | Inline source citations |
9494
| `loader` | ❌ 未完成 | Loading states for AI operations |

apps/registry/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"dev": "nitro dev --port 3001"
88
},
99
"devDependencies": {
10-
"@vue/compiler-sfc": "^3.5.21",
10+
"@vue/compiler-sfc": "^3.5.22",
1111
"h3": "^1.15.4",
1212
"nitropack": "^2.12.6",
1313
"ts-morph": "^27.0.0"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<script setup lang="ts">
2+
import { CodeBlock } from '@repo/elements/code-block'
3+
4+
const code = `<template>
5+
<div>
6+
<h1>Hello, {{ name }}!</h1>
7+
<p>This is an example Vue component.</p>
8+
</div>
9+
</template>
10+
11+
<script setup lang="ts">
12+
interface Props {
13+
name: string
14+
}
15+
16+
defineProps<Props>()
17+
<\/script>`
18+
</script>
19+
20+
<template>
21+
<CodeBlock :code="code" lang="vue" />
22+
</template>

apps/test/app/pages/index.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@repo/shadcn-vue/compo
33
import ActionsHover from '~/examples/actions-hover.vue'
44
import Actions from '~/examples/actions.vue'
55
import Branch from '~/examples/branch.vue'
6+
import CodeBlock from '~/examples/code-block.vue'
67
import Conversation from '~/examples/conversation.vue'
78
import MessageMarkdown from '~/examples/message-markdown.vue'
89
import Message from '~/examples/message.vue'
@@ -18,6 +19,7 @@ const components = [
1819
{ name: 'Conversation', Component: Conversation },
1920
{ name: 'Response', Component: Response },
2021
{ name: 'MessageMarkdown', Component: MessageMarkdown },
22+
{ name: 'CodeBlock', Component: CodeBlock },
2123
]
2224
</script>
2325

apps/test/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,22 @@
1313
"dependencies": {
1414
"@repo/elements": "workspace:*",
1515
"@repo/shadcn-vue": "workspace:*",
16-
"@tailwindcss/vite": "^4.1.13",
16+
"@tailwindcss/vite": "^4.1.14",
1717
"@vueuse/core": "^13.9.0",
18-
"ai": "^5.0.51",
18+
"ai": "^5.0.60",
1919
"class-variance-authority": "^0.7.1",
2020
"clsx": "^2.1.1",
2121
"lucide-vue-next": "^0.544.0",
2222
"nanoid": "^5.1.6",
2323
"nuxt": "^4.1.2",
24-
"shadcn-nuxt": "2.2.0",
24+
"shadcn-nuxt": "2.3.1",
2525
"tailwind-merge": "^3.3.1",
26-
"tailwindcss": "^4.1.13",
26+
"tailwindcss": "^4.1.14",
2727
"tw-animate-css": "^1.4.0",
28-
"vue": "^3.5.21",
28+
"vue": "^3.5.22",
2929
"vue-router": "^4.5.1"
3030
},
3131
"devDependencies": {
32-
"typescript": "^5.9.2"
32+
"typescript": "^5.9.3"
3333
}
3434
}

apps/www/content/2.components/6.branch.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ icon: lucide:git-branch
66

77
The `Branch` component manages multiple versions of AI messages, allowing users to navigate between different response branches. It provides a clean, modern interface with customizable themes and keyboard-accessible navigation buttons.
88

9-
::::ComponentLoader{label="Branch" componentName="Branch"}
10-
::::
9+
:::ComponentLoader{label="Branch" componentName="Branch"}
10+
:::
1111

1212
## Install using CLI
1313

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
---
2+
title: Code Block
3+
description:
4+
icon: lucide:code
5+
---
6+
7+
The `CodeBlock` component provides syntax highlighting and copy to clipboard functionality for code blocks. It uses Shiki for syntax highlighting and includes automatic light/dark theme switching.
8+
9+
## Install using CLI
10+
11+
:::tabs{variant="card"}
12+
::div{label="ai-elements-vue"}
13+
```sh
14+
npx ai-elements-vue@latest add code-block
15+
```
16+
::
17+
::div{label="shadcn-vue"}
18+
19+
```sh
20+
npx shadcn-vue@latest add https://registry.ai-elements-vue.com/code-block.json
21+
```
22+
::
23+
:::
24+
25+
## Install Manually
26+
27+
Copy and paste the following files into the same folder.
28+
29+
:::code-group
30+
```vue [CodeBlock.vue]
31+
<script setup lang="ts">
32+
import type { BuiltinTheme, BundledLanguage } from 'shiki'
33+
import { transformerCopyButton } from '@selemondev/shiki-transformer-copy-button'
34+
import CodeBlock from 'shiki-block-vue'
35+
36+
interface Props {
37+
code: string
38+
lang: BundledLanguage
39+
}
40+
41+
const props = withDefaults(defineProps<Props>(), {})
42+
43+
const theme: { light: BuiltinTheme, dark: BuiltinTheme } = {
44+
light: 'vitesse-light',
45+
dark: 'vitesse-dark',
46+
}
47+
</script>
48+
49+
<template>
50+
<CodeBlock
51+
:lang="props.lang"
52+
:code="props.code"
53+
:theme="theme"
54+
class="overflow-hidden"
55+
:transformers="[
56+
transformerCopyButton({
57+
duration: 3000,
58+
display: 'ready',
59+
successIcon: `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='rgba(128,128,128,1)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Crect width='8' height='4' x='8' y='2' rx='1' ry='1'/%3E%3Cpath d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'/%3E%3Cpath d='m9 14 2 2 4-4'/%3E%3C/svg%3E`,
60+
copyIcon: `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='rgba(128,128,128,1)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Crect width='8' height='4' x='8' y='2' rx='1' ry='1'/%3E%3Cpath d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'/%3E%3Cpath d='m9 14 2 2 4-4'/%3E%3C/svg%3E`,
61+
}),
62+
]"
63+
/>
64+
</template>
65+
66+
<style scoped>
67+
/* Dark Mode */
68+
@media (prefers-color-scheme: dark) {
69+
:deep(.shiki),
70+
:deep(.shiki span) {
71+
color: var(--shiki-dark) !important;
72+
background-color: var(--shiki-dark-bg) !important;
73+
/* Optional, if you also want font styles */
74+
font-style: var(--shiki-dark-font-style) !important;
75+
font-weight: var(--shiki-dark-font-weight) !important;
76+
text-decoration: var(--shiki-dark-text-decoration) !important;
77+
}
78+
}
79+
80+
html.dark :deep(.shiki),
81+
html.dark :deep(.shiki span) {
82+
color: var(--shiki-dark) !important;
83+
background-color: var(--shiki-dark-bg) !important;
84+
font-style: var(--shiki-dark-font-style) !important;
85+
font-weight: var(--shiki-dark-font-weight) !important;
86+
text-decoration: var(--shiki-dark-text-decoration) !important;
87+
}
88+
89+
:deep(.shiki--code--block) {
90+
width: 100%;
91+
}
92+
93+
:deep(pre) {
94+
z-index: 1;
95+
padding: 24px;
96+
border-radius: 10px;
97+
overflow-x: auto;
98+
-ms-overflow-style: none;
99+
scrollbar-width: none;
100+
position: relative;
101+
background-color: #F9F9F9 !important;
102+
}
103+
104+
:deep(code) {
105+
display: block;
106+
line-height: 1.7;
107+
font-size: 15px;
108+
}
109+
</style>
110+
```
111+
112+
```ts [index.ts]
113+
export { default as CodeBlock } from './CodeBlock.vue'
114+
```
115+
:::
116+
117+
## Usage
118+
119+
```vue
120+
<script setup lang="ts">
121+
import { CodeBlock } from '@/components/ai-elements/code-block'
122+
123+
const code = `<template>
124+
<div>
125+
<h1>Hello, {{ name }}!</h1>
126+
<p>This is an example Vue component.</p>
127+
</div>
128+
</template>
129+
130+
<script setup lang="ts">
131+
interface Props {
132+
name: string
133+
}
134+
135+
defineProps<Props>()
136+
<\/script>`
137+
</script>
138+
139+
<template>
140+
<CodeBlock :code="code" lang="vue" />
141+
</template>
142+
```
143+
144+
## Features
145+
146+
- Syntax highlighting with Shiki
147+
- Copy to clipboard functionality
148+
- Automatic light/dark theme switching
149+
- Support for all Shiki bundled languages
150+
- Customizable themes (vitesse-light/vitesse-dark)
151+
- Responsive design with horizontal scrolling
152+
- Clean, modern styling
153+
- Accessible design
154+
155+
## Props
156+
157+
### `<CodeBlock />`
158+
159+
:::field-group
160+
::field{name="code" type="string" required}
161+
The code content to display.
162+
::
163+
164+
::field{name="lang" type="BundledLanguage" required}
165+
The programming language for syntax highlighting. Supports all Shiki bundled languages.
166+
::
167+
:::

apps/www/nuxt.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ export default defineNuxtConfig({
1313

1414
components: [
1515
{ path: '~/components' },
16-
{ path: '../../packages/examples/src', pathPrefix: false },
1716
],
1817

1918
i18n: {

apps/www/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
"@repo/examples": "workspace:*",
1616
"nuxt": "3.18.0",
1717
"shadcn-docs-nuxt": "^1.1.2",
18-
"tailwindcss": "^4.1.13",
19-
"vue": "^3.5.21",
18+
"tailwindcss": "^4.1.14",
19+
"vue": "^3.5.22",
2020
"vue-router": "^4.5.1"
2121
},
2222
"devDependencies": {
2323
"@iconify-json/logos": "^1.2.9",
24-
"@iconify-json/simple-icons": "^1.2.53",
24+
"@iconify-json/simple-icons": "^1.2.54",
2525
"tailwindcss-animate": "^1.0.7"
2626
}
2727
}

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "ai-elements-vue",
33
"version": "0.3.0",
44
"private": true,
5-
"packageManager": "pnpm@10.17.1",
5+
"packageManager": "pnpm@10.18.0",
66
"engines": {
77
"node": ">=20.19.0"
88
},
@@ -22,14 +22,14 @@
2222
},
2323
"devDependencies": {
2424
"@antfu/eslint-config": "^5.4.1",
25-
"@commitlint/cli": "^19.8.1",
26-
"@commitlint/config-conventional": "^19.8.1",
27-
"bumpp": "^10.2.3",
28-
"eslint": "^9.36.0",
29-
"lint-staged": "^16.2.0",
25+
"@commitlint/cli": "^20.1.0",
26+
"@commitlint/config-conventional": "^20.0.0",
27+
"bumpp": "^10.3.1",
28+
"eslint": "^9.37.0",
29+
"lint-staged": "^16.2.3",
3030
"simple-git-hooks": "^2.13.1",
3131
"turbo": "^2.5.8",
32-
"typescript": "^5.9.2"
32+
"typescript": "^5.9.3"
3333
},
3434
"pnpm": {
3535
"allowedDeprecatedVersions": {

0 commit comments

Comments
 (0)