Skip to content

Commit

Permalink
lint:prettier
Browse files Browse the repository at this point in the history
  • Loading branch information
chihebnabil committed Nov 22, 2024
1 parent 6a9939a commit 395bf9e
Show file tree
Hide file tree
Showing 43 changed files with 1,142 additions and 905 deletions.
2 changes: 1 addition & 1 deletion .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github: chihebnabil
github: chihebnabil
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Claude UI

A modern chat interface for Anthropic's Claude AI models built with Nuxt.js. Experience seamless conversations with Claude in a clean user interface.

## Prerequisites
Expand All @@ -12,6 +13,7 @@ A modern chat interface for Anthropic's Claude AI models built with Nuxt.js. Exp
</p>

## 🌟 Features

- 💾 Conversation history management
- 🎭 Multiple Claude model support
- 📝 Markdown and code syntax highlighting
Expand All @@ -23,6 +25,7 @@ A modern chat interface for Anthropic's Claude AI models built with Nuxt.js. Exp
- 📝🔍 Text extraction and parsing

## Tech Stack

- 🚀 Built with [Nuxt 3](https://nuxt.com/)
- 💾 Database integration with [Drizzle ORM](https://orm.drizzle.team/)
- 🎨 UI components from [@nuxt/ui](https://ui.nuxt.com/)
Expand Down
56 changes: 31 additions & 25 deletions components/AuthForm.vue
Original file line number Diff line number Diff line change
@@ -1,35 +1,41 @@
<template>
<form class="mt-8 space-y-6" @submit.prevent="onSubmit">
<slot />
<div class="flex items-center justify-between">
<UCheckbox v-model="rememberMe" label="Remember me" name="remember-me" />
<div class="text-sm">
<UButton v-if="linkPath" variant="link" :to="linkPath">
{{ linkText }}
</UButton>
</div>
</div>
<div>
<UButton type="submit" block color="primary" :loading="isLoading" size="lg">
<template #leading>
<UIcon name="i-heroicons-lock-closed" />
</template>
{{ buttonText }}
</UButton>
</div>
</form>
<form class="mt-8 space-y-6" @submit.prevent="onSubmit">
<slot />
<div class="flex items-center justify-between">
<UCheckbox v-model="rememberMe" label="Remember me" name="remember-me" />
<div class="text-sm">
<UButton v-if="linkPath" variant="link" :to="linkPath">
{{ linkText }}
</UButton>
</div>
</div>
<div>
<UButton
type="submit"
block
color="primary"
:loading="isLoading"
size="lg"
>
<template #leading>
<UIcon name="i-heroicons-lock-closed" />
</template>
{{ buttonText }}
</UButton>
</div>
</form>
</template>

<script setup>
import { ref } from 'vue'
import { ref } from "vue";
defineProps({
onSubmit: Function,
buttonText: String,
linkText: String,
linkPath: String
onSubmit: Function,
buttonText: String,
linkText: String,
linkPath: String,
});
const rememberMe = ref(false);
const isLoading = ref(false);
</script>
</script>
127 changes: 70 additions & 57 deletions components/ChatMessage.vue
Original file line number Diff line number Diff line change
@@ -1,71 +1,84 @@
<template>
<div :class="[
'flex mb-4 max-w-full',
message.role === 'user' ? 'justify-end' : 'justify-start'
]">
<div :class="[
'flex items-start max-w-[85%]',
message.role === 'user' ? 'flex-row-reverse' : ''
]">
<UAvatar :alt="message.role === 'user' ? 'User' : 'AI'" size="sm" class="flex-shrink-0" />
<div class="flex flex-col mx-2">
<div :class="[
'px-4 py-2 rounded-lg break-words',
message.role === 'user'
? 'bg-primary text-white dark:bg-primary-600'
: 'bg-gray-200 dark:bg-gray-700 dark:text-gray-100'
]" v-html="$mdRenderer.render(message.content)" />
<span :class="[
'text-xs mt-1',
message.role === 'user' ? 'text-right' : 'text-left',
'text-gray-500 dark:text-gray-400'
]">
{{ relativeTime }}
</span>
</div>
</div>
<div
:class="[
'flex mb-4 max-w-full',
message.role === 'user' ? 'justify-end' : 'justify-start',
]"
>
<div
:class="[
'flex items-start max-w-[85%]',
message.role === 'user' ? 'flex-row-reverse' : '',
]"
>
<UAvatar
:alt="message.role === 'user' ? 'User' : 'AI'"
size="sm"
class="flex-shrink-0"
/>
<div class="flex flex-col mx-2">
<div
:class="[
'px-4 py-2 rounded-lg break-words',
message.role === 'user'
? 'bg-primary text-white dark:bg-primary-600'
: 'bg-gray-200 dark:bg-gray-700 dark:text-gray-100',
]"
v-html="$mdRenderer.render(message.content)"
/>
<span
:class="[
'text-xs mt-1',
message.role === 'user' ? 'text-right' : 'text-left',
'text-gray-500 dark:text-gray-400',
]"
>
{{ relativeTime }}
</span>
</div>
</div>
</div>
</template>

<script setup>
import { computed } from 'vue'
const { $mdRenderer } = useNuxtApp()
import { computed } from "vue";
const { $mdRenderer } = useNuxtApp();
const props = defineProps({
message: {
type: Object,
required: true,
validator: (value) => {
return value.role && value.content && value.createdAt
}
}
})
message: {
type: Object,
required: true,
validator: (value) => {
return value.role && value.content && value.createdAt;
},
},
});
const relativeTime = computed(() => {
const now = new Date()
const createdAt = new Date(props.message.createdAt)
const diffInSeconds = Math.floor((now - createdAt) / 1000)
const now = new Date();
const createdAt = new Date(props.message.createdAt);
const diffInSeconds = Math.floor((now - createdAt) / 1000);
if (diffInSeconds < 60) {
return 'just now'
}
if (diffInSeconds < 60) {
return "just now";
}
const diffInMinutes = Math.floor(diffInSeconds / 60)
if (diffInMinutes < 60) {
return `${diffInMinutes}m ago`
}
const diffInMinutes = Math.floor(diffInSeconds / 60);
if (diffInMinutes < 60) {
return `${diffInMinutes}m ago`;
}
const diffInHours = Math.floor(diffInMinutes / 60)
if (diffInHours < 24) {
return `${diffInHours}h ago`
}
const diffInHours = Math.floor(diffInMinutes / 60);
if (diffInHours < 24) {
return `${diffInHours}h ago`;
}
const diffInDays = Math.floor(diffInHours / 24)
if (diffInDays < 7) {
return `${diffInDays}d ago`
}
const diffInDays = Math.floor(diffInHours / 24);
if (diffInDays < 7) {
return `${diffInDays}d ago`;
}
// For older messages, show the actual date
return createdAt.toLocaleDateString()
})
</script>
// For older messages, show the actual date
return createdAt.toLocaleDateString();
});
</script>
137 changes: 87 additions & 50 deletions components/ColorPicker.vue
Original file line number Diff line number Diff line change
@@ -1,62 +1,99 @@
<template>
<UPopover mode="hover" :popper="{ strategy: 'absolute' }" :ui="{ width: 'w-[156px]' }">
<template #default="{ open }">
<UButton
color="gray" variant="ghost" square :class="[open && 'bg-gray-50 dark:bg-gray-800']"
aria-label="Color picker">
<UIcon name="i-heroicons-swatch-20-solid" class="w-5 h-5 text-primary-500 dark:text-primary-400" />
</UButton>
</template>

<template #panel>
<div class="p-2">
<div class="grid grid-cols-5 gap-px">
<ColorPickerPill
v-for="color in primaryColors" :key="color.value" :color="color"
:selected="primary" @select="primary = color" />
</div>

<hr class="border-gray-200 dark:border-gray-800 my-2">

<div class="grid grid-cols-5 gap-px">
<ColorPickerPill
v-for="color in grayColors" :key="color.value" :color="color" :selected="gray"
@select="gray = color" />
</div>
</div>
</template>
</UPopover>
<UPopover
mode="hover"
:popper="{ strategy: 'absolute' }"
:ui="{ width: 'w-[156px]' }"
>
<template #default="{ open }">
<UButton
color="gray"
variant="ghost"
square
:class="[open && 'bg-gray-50 dark:bg-gray-800']"
aria-label="Color picker"
>
<UIcon
name="i-heroicons-swatch-20-solid"
class="w-5 h-5 text-primary-500 dark:text-primary-400"
/>
</UButton>
</template>

<template #panel>
<div class="p-2">
<div class="grid grid-cols-5 gap-px">
<ColorPickerPill
v-for="color in primaryColors"
:key="color.value"
:color="color"
:selected="primary"
@select="primary = color"
/>
</div>

<hr class="border-gray-200 dark:border-gray-800 my-2" />

<div class="grid grid-cols-5 gap-px">
<ColorPickerPill
v-for="color in grayColors"
:key="color.value"
:color="color"
:selected="gray"
@select="gray = color"
/>
</div>
</div>
</template>
</UPopover>
</template>

<script setup lang="ts">
import colors from '#tailwind-config/theme/colors'
import colors from "#tailwind-config/theme/colors";
const appConfig = useAppConfig()
const colorMode = useColorMode()
const appConfig = useAppConfig();
const colorMode = useColorMode();
// Computed
const primaryColors = computed(() => appConfig.ui.colors.filter(color => color !== 'primary').map(color => ({ value: color, text: color, hex: colors[color][colorMode.value === 'dark' ? 400 : 500] })))
const primaryColors = computed(() =>
appConfig.ui.colors
.filter((color) => color !== "primary")
.map((color) => ({
value: color,
text: color,
hex: colors[color][colorMode.value === "dark" ? 400 : 500],
})),
);
const primary = computed({
get() {
return primaryColors.value.find(option => option.value === appConfig.ui.primary)
},
set(option) {
appConfig.ui.primary = option.value
get() {
return primaryColors.value.find(
(option) => option.value === appConfig.ui.primary,
);
},
set(option) {
appConfig.ui.primary = option.value;
window.localStorage.setItem('nuxt-ui-primary', appConfig.ui.primary)
}
})
window.localStorage.setItem("nuxt-ui-primary", appConfig.ui.primary);
},
});
const grayColors = computed(() => ['slate', 'cool', 'zinc', 'neutral', 'stone'].map(color => ({ value: color, text: color, hex: colors[color][colorMode.value === 'dark' ? 400 : 500] })))
const grayColors = computed(() =>
["slate", "cool", "zinc", "neutral", "stone"].map((color) => ({
value: color,
text: color,
hex: colors[color][colorMode.value === "dark" ? 400 : 500],
})),
);
const gray = computed({
get() {
return grayColors.value.find(option => option.value === appConfig.ui.gray)
},
set(option) {
appConfig.ui.gray = option.value
window.localStorage.setItem('nuxt-ui-gray', appConfig.ui.gray)
}
})
</script>
get() {
return grayColors.value.find(
(option) => option.value === appConfig.ui.gray,
);
},
set(option) {
appConfig.ui.gray = option.value;
window.localStorage.setItem("nuxt-ui-gray", appConfig.ui.gray);
},
});
</script>
Loading

0 comments on commit 395bf9e

Please sign in to comment.