Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/test/app/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export { default as MessageMarkdown } from './message-markdown.vue'
export { default as Message } from './message.vue'
export { default as PromptInput } from './prompt-input.vue'
export { default as Response } from './response.vue'
export { default as Shimmer } from './shimmer.vue'
15 changes: 15 additions & 0 deletions apps/test/app/examples/shimmer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script setup lang="ts">
import { Shimmer } from '@repo/elements/shimmer'
</script>

<template>
<div class="flex flex-col items-center justify-center gap-4 p-8">
<Shimmer>This text has a shimmer effect</Shimmer>
<Shimmer as="h1" class="font-bold text-4xl">
Large Heading
</Shimmer>
<Shimmer :duration="3" :spread="3">
Slower shimmer with wider spread
</Shimmer>
</div>
</template>
2 changes: 2 additions & 0 deletions apps/test/app/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import MessageMarkdown from '~/examples/message-markdown.vue'
import Message from '~/examples/message.vue'
import PromptInput from '~/examples/prompt-input.vue'
import Response from '~/examples/response.vue'
import Shimmer from '~/examples/shimmer.vue'

const components = [
{ name: 'Message', Component: Message },
Expand All @@ -22,6 +23,7 @@ const components = [
{ name: 'MessageMarkdown', Component: MessageMarkdown },
{ name: 'CodeBlock', Component: CodeBlock },
{ name: 'Image', Component: Image },
{ name: 'Shimmer', Component: Shimmer },
]
</script>

Expand Down
189 changes: 189 additions & 0 deletions apps/www/assets/css/code-theme.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/* Base styles for light mode (Tailwind default mode) */
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
code.hljs {
padding: 3px 5px;
}
.hljs {
color: #24292e;
background: #ffffff;
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
color: #d73a49;
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
color: #6f42c1;
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
color: #005cc5;
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
color: #032f62;
}
.hljs-built_in,
.hljs-symbol {
color: #e36209;
}
.hljs-comment,
.hljs-code,
.hljs-formula {
color: #6a737d;
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
color: #22863a;
}
.hljs-subst {
color: #24292e;
}
.hljs-section {
color: #005cc5;
font-weight: bold;
}
.hljs-bullet {
color: #735c0f;
}
.hljs-emphasis {
color: #24292e;
font-style: italic;
}
.hljs-strong {
color: #24292e;
font-weight: bold;
}
.hljs-addition {
color: #22863a;
background-color: #f0fff4;
}
.hljs-deletion {
color: #b31d28;
background-color: #ffeef0;
}
.hljs-char.escape_,
.hljs-link,
.hljs-params,
.hljs-property,
.hljs-punctuation,
.hljs-tag {
/* purposely ignored */
}

/* Dark mode styles using Tailwind's `dark:` prefix */
.dark pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
.dark code.hljs {
padding: 3px 5px;
}
.dark .hljs {
color: #c9d1d9;
background: #0d1117;
}
.dark .hljs-doctag,
.dark .hljs-keyword,
.dark .hljs-meta .hljs-keyword,
.dark .hljs-template-tag,
.dark .hljs-template-variable,
.dark .hljs-type,
.dark .hljs-variable.language_ {
color: #ff7b72;
}
.dark .hljs-title,
.dark .hljs-title.class_,
.dark .hljs-title.class_.inherited__,
.dark .hljs-title.function_ {
color: #d2a8ff;
}
.dark .hljs-attr,
.dark .hljs-attribute,
.dark .hljs-literal,
.dark .hljs-meta,
.dark .hljs-number,
.dark .hljs-operator,
.dark .hljs-variable,
.dark .hljs-selector-attr,
.dark .hljs-selector-class,
.dark .hljs-selector-id {
color: #79c0ff;
}
.dark .hljs-regexp,
.dark .hljs-string,
.dark .hljs-meta .hljs-string {
color: #a5d6ff;
}
.dark .hljs-built_in,
.dark .hljs-symbol {
color: #ffa657;
}
.dark .hljs-comment,
.dark .hljs-code,
.dark .hljs-formula {
color: #8b949e;
}
.dark .hljs-name,
.dark .hljs-quote,
.dark .hljs-selector-tag,
.dark .hljs-selector-pseudo {
color: #7ee787;
}
.dark .hljs-subst {
color: #c9d1d9;
}
.dark .hljs-section {
color: #1f6feb;
font-weight: bold;
}
.dark .hljs-bullet {
color: #f2cc60;
}
.dark .hljs-emphasis {
color: #c9d1d9;
font-style: italic;
}
.dark .hljs-strong {
color: #c9d1d9;
font-weight: bold;
}
.dark .hljs-addition {
color: #aff5b4;
background-color: #033a16;
}
.dark .hljs-deletion {
color: #ffdcd7;
background-color: #67060c;
}
.dark .hljs-char.escape_,
.dark .hljs-link,
.dark .hljs-params,
.dark .hljs-property,
.dark .hljs-punctuation,
.dark .hljs-tag {
/* purposely ignored */
}
10 changes: 10 additions & 0 deletions apps/www/assets/css/tailwind.css
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,14 @@
body {
@apply bg-background text-foreground;
}
:root {
--color-background: hsl(var(--background));
--color-muted-foreground: hsl(var(--muted-foreground));
}
.dark {
--color-background: hsl(var(--background));
--color-muted-foreground: hsl(var(--muted-foreground));
}
}


107 changes: 107 additions & 0 deletions apps/www/components/CodeViewerTab.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<!-- eslint-disable vue/no-v-html -->
<script setup lang="ts">
import { cn } from '@repo/shadcn-vue/lib/utils'
import hljs from 'highlight.js'
import { computed, onMounted, ref } from 'vue'
import { MagicString } from 'vue/compiler-sfc'
import '~/assets/css/code-theme.css'

interface Props {
componentName?: string
id?: string
type?: string
icon?: string
class?: string
extension?: string
}

const props = withDefaults(defineProps<Props>(), {
icon: 'lucide:square-terminal',
extension: 'vue',
})
const rawString = ref('')
const codeHtml = ref('')

// Create a map of all possible components using import.meta.glob
const rawComponents = import.meta.glob('../../../packages/examples/src/**/*.{vue,ts,js,d.ts}', {
query: '?raw',
import: 'default',
})

// helper to convert componentName to a filename in kebab-case
function toFileName(name?: string, ext?: string) {
if (!name)
throw new Error('componentName is required')

const kebab = name
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
.replace(/[\s_]+/g, '-')
.toLowerCase()

return `${kebab}.${ext || 'vue'}`
}

// Compute the component path based on props
const componentPath = computed(
() => `../../../packages/examples/src/${toFileName(props.componentName, props.extension)}`,
)

// Load and process the component code on mount
onMounted(() => {
loadAndProcessComponentCode()
})

// Function to load and process the component code
async function loadAndProcessComponentCode() {
try {
const componentCode = await fetchComponentCode()
rawString.value = updateImportPaths(componentCode)
codeHtml.value = hljs.highlightAuto(rawString.value, ['ts', 'html', 'css', 'js', 'd.ts']).value
}
catch (error: any) {
throw new Error('Error loading component code:', error)
}
}

// Fetch the raw code of the component dynamically
async function fetchComponentCode() {
const path = componentPath.value
const loadRawComponent = rawComponents[path]
if (!loadRawComponent)
throw new Error(`Component not found: ${path}`)

const mod = await loadRawComponent()
// Ensure 'mod' is string before calling trim, or handle if not string.
if (typeof mod !== 'string') {
throw new TypeError('Raw component code is not a string')
}
return mod.trim()
}

// Update import paths in the raw code using MagicString
function updateImportPaths(code: string) {
const magicString = new MagicString(code)
magicString.replaceAll('@repo/elements/', '@/components/ai-elements/')
magicString.replaceAll('~/composables/', '@/composables/')
return magicString.toString()
}
</script>

<template>
<div
:icon="icon"
:class="cn('relative flex max-h-[32rem]', $props.class)"
:code="rawString"
>
<CodeCopy
class="absolute -top-10 right-0"
:code="rawString"
/>
<code class="min-w-full overflow-auto px-2 leading-4">
<pre
class="text-sm"
v-html="codeHtml"
/>
</code>
</div>
</template>
4 changes: 4 additions & 0 deletions apps/www/components/ComponentLoader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ withDefaults(defineProps<Props>(), {
>
<ComponentViewer :component-name="componentName" />
</div>
<CodeViewerTab
v-bind="$props"
label="Code"
/>
</CodeGroup>
</ClientOnly>
</div>
Expand Down
Loading