mirror of
https://github.com/abhinavxd/libredesk.git
synced 2026-09-23 02:53:32 +00:00
improve conversation thread and inbox list ux
Add expand button for long message bubbles, group consecutive same-sender messages within a minute, and fix the conversation list highlight contrast (Fixes #321). Also bumps vue-letter to 0.2.1 to allow CSS transforms in email content
This commit is contained in:
@@ -3,10 +3,11 @@
|
||||
<ContextMenuTrigger asChild>
|
||||
<router-link
|
||||
:to="conversationRoute"
|
||||
class="group relative block px-3 py-3 transition-all duration-200 ease-in-out cursor-pointer hover:bg-accent/20 dark:hover:bg-accent/60"
|
||||
class="group relative block px-3 py-3 transition-all duration-200 ease-in-out cursor-pointer"
|
||||
:class="{
|
||||
'bg-accent/60': conversation.uuid === currentConversation?.uuid,
|
||||
'bg-primary/5': isItemSelected && conversation.uuid !== currentConversation?.uuid
|
||||
'bg-accent': isCurrent,
|
||||
'bg-primary/5 hover:bg-primary/10': isItemSelected && !isCurrent,
|
||||
'hover:bg-accent/40': !isCurrent && !isItemSelected
|
||||
}"
|
||||
>
|
||||
<div class="flex items-start gap-2">
|
||||
@@ -51,9 +52,14 @@
|
||||
<!-- Contact name + inbox + time -->
|
||||
<div class="flex items-baseline justify-between gap-2">
|
||||
<div class="flex items-baseline gap-1.5 min-w-0">
|
||||
<h3 class="text-sm font-semibold truncate text-foreground">
|
||||
{{ contactFullName }}
|
||||
</h3>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<h3 class="text-sm font-semibold truncate text-foreground">
|
||||
{{ contactFullName }}
|
||||
</h3>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{{ contactFullName }}</TooltipContent>
|
||||
</Tooltip>
|
||||
<span class="text-xs text-muted-foreground truncate">
|
||||
{{ conversation.inbox_name }}
|
||||
</span>
|
||||
@@ -154,6 +160,7 @@ import {
|
||||
ContextMenuTrigger
|
||||
} from '@shared-ui/components/ui/context-menu'
|
||||
import SlaBadge from '@main/features/sla/SlaBadge.vue'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@shared-ui/components/ui/tooltip'
|
||||
import { Checkbox } from '@shared-ui/components/ui/checkbox'
|
||||
import { useConversationStore } from '@main/stores/conversation'
|
||||
import { useBulkActionPermissions } from '@/composables/useBulkActionPermissions'
|
||||
@@ -235,6 +242,8 @@ const draftPreview = computed(() => {
|
||||
return text.length > 120 ? text.slice(0, 120) + '...' : text
|
||||
})
|
||||
|
||||
const isCurrent = computed(() => props.conversation.uuid === props.currentConversation?.uuid)
|
||||
|
||||
const isItemSelected = computed(() => {
|
||||
return conversationStore.isSelected(props.conversation.uuid)
|
||||
})
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<div class="flex flex-col text-left" :class="isOutgoing ? 'items-end' : 'items-start'">
|
||||
<!-- Sender Name -->
|
||||
<div class="mb-1 flex items-center gap-1" :class="isOutgoing ? 'pr-[47px]' : 'pl-[47px]'">
|
||||
<div
|
||||
v-if="!groupWithPrev"
|
||||
class="mb-1 flex items-center gap-1"
|
||||
:class="isOutgoing ? 'pr-[47px]' : 'pl-[47px]'"
|
||||
>
|
||||
<router-link
|
||||
v-if="!isOutgoing"
|
||||
:to="{ name: 'contact-detail', params: { id: message.author?.id } }"
|
||||
@@ -24,18 +28,21 @@
|
||||
<!-- Message Bubble -->
|
||||
<div class="flex flex-row gap-2 w-full" :class="{ 'justify-end': isOutgoing }">
|
||||
<!-- Avatar (left for incoming) -->
|
||||
<router-link
|
||||
v-if="!isOutgoing"
|
||||
:to="{ name: 'contact-detail', params: { id: message.author?.id } }"
|
||||
class="flex-shrink-0"
|
||||
>
|
||||
<Avatar class="cursor-pointer w-8 h-8 hover:opacity-80 transition-opacity">
|
||||
<AvatarImage :src="getAvatar" />
|
||||
<AvatarFallback class="font-medium">
|
||||
{{ avatarFallback }}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
</router-link>
|
||||
<template v-if="!isOutgoing">
|
||||
<router-link
|
||||
v-if="!groupWithPrev"
|
||||
:to="{ name: 'contact-detail', params: { id: message.author?.id } }"
|
||||
class="flex-shrink-0"
|
||||
>
|
||||
<Avatar class="cursor-pointer w-8 h-8 hover:opacity-80 transition-opacity">
|
||||
<AvatarImage :src="getAvatar" />
|
||||
<AvatarFallback class="font-medium">
|
||||
{{ avatarFallback }}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
</router-link>
|
||||
<div v-else class="w-8 flex-shrink-0" />
|
||||
</template>
|
||||
|
||||
<!-- Bubble Wrapper with max 80% width -->
|
||||
<div
|
||||
@@ -54,19 +61,45 @@
|
||||
|
||||
<!-- Message Content -->
|
||||
<div
|
||||
v-if="message.content_type === 'text'"
|
||||
class="mb-1 native-html whitespace-pre-wrap"
|
||||
:class="{ 'mb-3': message.attachments.length > 0 }"
|
||||
ref="contentWrapperEl"
|
||||
class="relative"
|
||||
:class="{ 'max-h-[400px] overflow-hidden': isExpandable && !isExpanded }"
|
||||
>
|
||||
{{ sanitizedContent }}
|
||||
</div>
|
||||
<div v-else ref="messageContentEl" @click="onMessageContentClick">
|
||||
<Letter
|
||||
:html="sanitizedContent"
|
||||
:allowedSchemas="['cid', 'https', 'http', 'mailto']"
|
||||
class="mb-1 native-html whitespace-pre-wrap break-words"
|
||||
<div
|
||||
v-if="message.content_type === 'text'"
|
||||
class="mb-1 native-html whitespace-pre-wrap"
|
||||
:class="{ 'mb-3': message.attachments.length > 0 }"
|
||||
/>
|
||||
>
|
||||
{{ sanitizedContent }}
|
||||
</div>
|
||||
<div v-else ref="messageContentEl" @click="onMessageContentClick">
|
||||
<Letter
|
||||
:html="sanitizedContent"
|
||||
:allowedSchemas="['cid', 'https', 'http', 'mailto']"
|
||||
:allowed-css-properties="extendedCssProperties"
|
||||
class="mb-1 native-html whitespace-pre-wrap break-words"
|
||||
:class="{ 'mb-3': message.attachments.length > 0 }"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="isExpandable && !isExpanded"
|
||||
class="absolute left-0 right-0 bottom-0 h-24 flex items-end justify-center pointer-events-none"
|
||||
:class="
|
||||
message.private
|
||||
? 'bg-gradient-to-t from-private via-private/90 to-transparent'
|
||||
: 'bg-gradient-to-t from-background via-background/90 to-transparent'
|
||||
"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
@click="isExpanded = true"
|
||||
class="pointer-events-auto flex items-center gap-1.5 text-xs font-medium text-foreground bg-accent hover:bg-accent/80 border border-border rounded-full px-3 py-1 mb-1 transition-colors duration-200"
|
||||
>
|
||||
<Maximize2 :size="12" />
|
||||
{{ t('globals.terms.expand') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ImageLightbox
|
||||
@@ -116,28 +149,31 @@
|
||||
</div>
|
||||
|
||||
<!-- Avatar (right for outgoing) -->
|
||||
<router-link
|
||||
v-if="isOutgoing && canManageUsers"
|
||||
:to="{ name: 'edit-agent', params: { id: message.author?.id } }"
|
||||
class="flex-shrink-0"
|
||||
>
|
||||
<Avatar class="cursor-pointer w-8 h-8 hover:opacity-80 transition-opacity">
|
||||
<template v-if="isOutgoing">
|
||||
<div v-if="groupWithPrev" class="w-8 flex-shrink-0" />
|
||||
<router-link
|
||||
v-else-if="canManageUsers"
|
||||
:to="{ name: 'edit-agent', params: { id: message.author?.id } }"
|
||||
class="flex-shrink-0"
|
||||
>
|
||||
<Avatar class="cursor-pointer w-8 h-8 hover:opacity-80 transition-opacity">
|
||||
<AvatarImage :src="getAvatar" />
|
||||
<AvatarFallback class="font-medium">
|
||||
{{ avatarFallback }}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
</router-link>
|
||||
<Avatar v-else class="w-8 h-8">
|
||||
<AvatarImage :src="getAvatar" />
|
||||
<AvatarFallback class="font-medium">
|
||||
{{ avatarFallback }}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
</router-link>
|
||||
<Avatar v-else-if="isOutgoing" class="w-8 h-8">
|
||||
<AvatarImage :src="getAvatar" />
|
||||
<AvatarFallback class="font-medium">
|
||||
{{ avatarFallback }}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Timestamp tooltip -->
|
||||
<div :class="isOutgoing ? 'pr-[47px]' : 'pl-[47px]'">
|
||||
<div v-if="!groupWithNext" :class="isOutgoing ? 'pr-[47px]' : 'pl-[47px]'">
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<span class="text-muted-foreground text-xs mt-1">
|
||||
@@ -153,27 +189,61 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, onMounted, nextTick } from 'vue'
|
||||
import { useConversationStore } from '@main/stores/conversation'
|
||||
import { useUserStore } from '@main/stores/user'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Lock, Mail, RotateCcw, Check } from 'lucide-vue-next'
|
||||
import { Lock, Mail, RotateCcw, Check, Maximize2 } from 'lucide-vue-next'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@shared-ui/components/ui/tooltip'
|
||||
import { Spinner } from '@shared-ui/components/ui/spinner'
|
||||
import { formatMessageTimestamp, formatFullTimestamp } from '@shared-ui/utils/datetime.js'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@shared-ui/components/ui/avatar'
|
||||
import { Letter } from 'vue-letter'
|
||||
import { allowedCssProperties } from 'lettersanitizer'
|
||||
import ImageLightbox from '@/components/ImageLightbox.vue'
|
||||
import BubbleAttachmentPreview from '@main/features/conversation/message/attachment/BubbleAttachmentPreview.vue'
|
||||
import MessageEnvelope from './MessageEnvelope.vue'
|
||||
import CSATResponseDisplay from './CSATResponseDisplay.vue'
|
||||
import api from '@main/api'
|
||||
|
||||
const extendedCssProperties = [...allowedCssProperties, 'transform', 'transform-origin']
|
||||
|
||||
const COLLAPSE_THRESHOLD_PX = 400
|
||||
|
||||
const contentWrapperEl = ref(null)
|
||||
const isExpandable = ref(false)
|
||||
const isExpanded = ref(false)
|
||||
|
||||
const measureExpandable = () => {
|
||||
const el = contentWrapperEl.value
|
||||
if (!el) return
|
||||
isExpandable.value = el.scrollHeight > COLLAPSE_THRESHOLD_PX
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
measureExpandable()
|
||||
|
||||
// Email HTML images change height after initial paint - re-measure on load.
|
||||
const imgs = contentWrapperEl.value?.querySelectorAll?.('img') ?? []
|
||||
imgs.forEach((img) => {
|
||||
if (!img.complete) img.addEventListener('load', measureExpandable, { once: true })
|
||||
})
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
message: Object,
|
||||
direction: {
|
||||
type: String,
|
||||
validator: (v) => ['incoming', 'outgoing'].includes(v)
|
||||
},
|
||||
groupWithPrev: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
groupWithNext: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -22,24 +22,31 @@
|
||||
|
||||
<MessagesSkeleton :count="10" v-if="conversationStore.messages.loading" />
|
||||
|
||||
<TransitionGroup v-else enter-active-class="animate-slide-in" tag="div" class="space-y-4">
|
||||
<TransitionGroup v-else enter-active-class="animate-slide-in" tag="div">
|
||||
<div
|
||||
v-for="(message, index) in conversationStore.conversationMessages"
|
||||
:key="message.uuid"
|
||||
:data-message-uuid="message.uuid"
|
||||
:class="{
|
||||
'my-2': message.type === 'activity',
|
||||
'pt-4': index === 0
|
||||
}"
|
||||
v-for="row in messageRows"
|
||||
:key="row.message.uuid"
|
||||
:data-message-uuid="row.message.uuid"
|
||||
:class="[row.spacingClass, { 'my-2': row.message.type === 'activity' }]"
|
||||
>
|
||||
<div v-if="!message.private && message.type !== 'activity'">
|
||||
<MessageBubble :message="message" :direction="message.type" />
|
||||
<div v-if="!row.message.private && row.message.type !== 'activity'">
|
||||
<MessageBubble
|
||||
:message="row.message"
|
||||
:direction="row.message.type"
|
||||
:group-with-prev="row.groupWithPrev"
|
||||
:group-with-next="row.groupWithNext"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="isPrivateNote(message)">
|
||||
<MessageBubble :message="message" direction="outgoing" />
|
||||
<div v-else-if="isPrivateNote(row.message)">
|
||||
<MessageBubble
|
||||
:message="row.message"
|
||||
direction="outgoing"
|
||||
:group-with-prev="row.groupWithPrev"
|
||||
:group-with-next="row.groupWithNext"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="message.type === 'activity'">
|
||||
<ActivityMessageBubble :message="message" />
|
||||
<div v-else-if="row.message.type === 'activity'">
|
||||
<ActivityMessageBubble :message="row.message" />
|
||||
</div>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
@@ -61,7 +68,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import MessageBubble from './MessageBubble.vue'
|
||||
import ActivityMessageBubble from './ActivityMessageBubble.vue'
|
||||
@@ -219,6 +226,43 @@ watch(
|
||||
const isPrivateNote = (message) => {
|
||||
return message.type === 'outgoing' && message.private
|
||||
}
|
||||
|
||||
const GROUP_WINDOW_MS = 60_000
|
||||
|
||||
const canGroup = (a, b) => {
|
||||
if (!a || !b) return false
|
||||
if (a.type === 'activity' || b.type === 'activity') return false
|
||||
if (a.type !== b.type) return false
|
||||
if (Boolean(a.private) !== Boolean(b.private)) return false
|
||||
if (a.status === 'failed' || b.status === 'failed') return false
|
||||
|
||||
const aSenderId = a.author?.id ?? a.sender_id
|
||||
const bSenderId = b.author?.id ?? b.sender_id
|
||||
if (!aSenderId || aSenderId !== bSenderId) return false
|
||||
|
||||
const aBucket = Math.floor(new Date(a.created_at).getTime() / GROUP_WINDOW_MS)
|
||||
const bBucket = Math.floor(new Date(b.created_at).getTime() / GROUP_WINDOW_MS)
|
||||
return aBucket === bBucket
|
||||
}
|
||||
|
||||
const getSpacingClass = (index, groupWithPrev) => {
|
||||
if (index === 0) return 'pt-4'
|
||||
return groupWithPrev ? 'mt-1' : 'mt-4'
|
||||
}
|
||||
|
||||
const messageRows = computed(() => {
|
||||
const messages = conversationStore.conversationMessages
|
||||
return messages.map((message, index) => {
|
||||
const groupWithPrev = canGroup(messages[index - 1], message)
|
||||
const groupWithNext = canGroup(message, messages[index + 1])
|
||||
return {
|
||||
message,
|
||||
groupWithPrev,
|
||||
groupWithNext,
|
||||
spacingClass: getSpacingClass(index, groupWithPrev)
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
v-else
|
||||
:html="message.content"
|
||||
:allowedSchemas="['cid', 'https', 'http', 'mailto']"
|
||||
:allowed-css-properties="extendedCssProperties"
|
||||
class="mb-1 native-html"
|
||||
/>
|
||||
<div
|
||||
@@ -151,6 +152,7 @@ import { useChatStore } from '../store/chat.js'
|
||||
import { useRelativeTime } from '@widget/composables/useRelativeTime.js'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Letter } from 'vue-letter'
|
||||
import { allowedCssProperties } from 'lettersanitizer'
|
||||
import ScrollToBottomButton from '@shared-ui/components/ScrollToBottomButton'
|
||||
import ChatIntro from './ChatIntro.vue'
|
||||
import NoticeBanner from './NoticeBanner.vue'
|
||||
@@ -159,6 +161,8 @@ import CSATMessageBubble from './CSATMessageBubble.vue'
|
||||
import { TypingIndicator } from '@shared-ui/components/TypingIndicator'
|
||||
import { Spinner } from '@shared-ui/components/ui/spinner'
|
||||
|
||||
const extendedCssProperties = [...allowedCssProperties, 'transform', 'transform-origin']
|
||||
|
||||
const props = defineProps({
|
||||
showPreChatForm: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"codemirror": "^6.0.2",
|
||||
"date-fns": "^3.6.0",
|
||||
"lettersanitizer": "^1.0.7",
|
||||
"lucide-vue-next": "^0.525.0",
|
||||
"mitt": "^3.0.1",
|
||||
"pinia": "^2.1.7",
|
||||
@@ -61,7 +62,7 @@
|
||||
"vue": "^3.4.37",
|
||||
"vue-easy-lightbox": "^1.19.0",
|
||||
"vue-i18n": "9",
|
||||
"vue-letter": "^0.2.0",
|
||||
"vue-letter": "^0.2.1",
|
||||
"vue-picture-cropper": "^0.7.0",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue-sonner": "^1.3.0",
|
||||
|
||||
Generated
+12
-9
@@ -98,6 +98,9 @@ importers:
|
||||
date-fns:
|
||||
specifier: ^3.6.0
|
||||
version: 3.6.0
|
||||
lettersanitizer:
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7
|
||||
lucide-vue-next:
|
||||
specifier: ^0.525.0
|
||||
version: 0.525.0(vue@3.5.13(typescript@5.7.3))
|
||||
@@ -132,8 +135,8 @@ importers:
|
||||
specifier: '9'
|
||||
version: 9.14.5(vue@3.5.13(typescript@5.7.3))
|
||||
vue-letter:
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0
|
||||
specifier: ^0.2.1
|
||||
version: 0.2.1
|
||||
vue-picture-cropper:
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0(vue@3.5.13(typescript@5.7.3))
|
||||
@@ -2777,8 +2780,8 @@ packages:
|
||||
leaflet@1.7.1:
|
||||
resolution: {integrity: sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==}
|
||||
|
||||
lettersanitizer@1.0.6:
|
||||
resolution: {integrity: sha512-2vj0tUtBRjlmTCFsgVlFmfi04p049Zwv/4eBGWBUOKropEoL+q+jTQQfFyDu50j7gL76pycMvrqD0uV0vxX2zg==}
|
||||
lettersanitizer@1.0.7:
|
||||
resolution: {integrity: sha512-r+P8T0WxmoKmm5F96tPAyYFT4ef5YDXTVmWtZjHD9FA/T8dtYM+Rb2uIxb4IerwrDxG/toYN2SQXFo4RUy4mxQ==}
|
||||
|
||||
levn@0.4.1:
|
||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||
@@ -3825,8 +3828,8 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
|
||||
vue-letter@0.2.0:
|
||||
resolution: {integrity: sha512-p4qHpw89GKidKyGcg4J4IjUcMQuVaTJq83c6UE4616claaLF9rj2Bg/Hq19uDCmokd+SvzEwxq6/ctfaaednkw==}
|
||||
vue-letter@0.2.1:
|
||||
resolution: {integrity: sha512-IYWp47XUikjKfEniWYlFxeJFKABZwAE5IEjz866qCBytBr2dzqVDdjoMDpBP//krxkzN/QZYyHe6C09y/IODYg==}
|
||||
|
||||
vue-picture-cropper@0.7.0:
|
||||
resolution: {integrity: sha512-NF7+Dgso6d0GB16E5d/BbrcTIHm1VWz8dS3IjLhoBl+ZeC+yDA46CyJphQuO32SisaPmrKHN8VbiE2LgAfhnkQ==}
|
||||
@@ -6650,7 +6653,7 @@ snapshots:
|
||||
|
||||
leaflet@1.7.1: {}
|
||||
|
||||
lettersanitizer@1.0.6: {}
|
||||
lettersanitizer@1.0.7: {}
|
||||
|
||||
levn@0.4.1:
|
||||
dependencies:
|
||||
@@ -7757,9 +7760,9 @@ snapshots:
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.13(typescript@5.7.3)
|
||||
|
||||
vue-letter@0.2.0:
|
||||
vue-letter@0.2.1:
|
||||
dependencies:
|
||||
lettersanitizer: 1.0.6
|
||||
lettersanitizer: 1.0.7
|
||||
|
||||
vue-picture-cropper@0.7.0(vue@3.5.13(typescript@5.7.3)):
|
||||
dependencies:
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
--sidebar-foreground: 240 5.9% 10%;
|
||||
--sidebar-primary: 240 5.9% 30%;
|
||||
--sidebar-primary-foreground: 0 0% 98%;
|
||||
--sidebar-accent: 240 4.8% 95.9%;
|
||||
--sidebar-accent: 240 5% 93%;
|
||||
--sidebar-accent-foreground: 240 5.9% 10%;
|
||||
--sidebar-border: 240 5% 88%;
|
||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||
@@ -113,7 +113,7 @@
|
||||
--sidebar-foreground: 0 0% 100%;
|
||||
--sidebar-primary: 235 86% 65%;
|
||||
--sidebar-primary-foreground: 0 0% 100%;
|
||||
--sidebar-accent: 240 3.7% 15.9%;
|
||||
--sidebar-accent: 240 5% 19%;
|
||||
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
||||
--sidebar-border: 240 5% 14%;
|
||||
--sidebar-ring: 235 86% 65%;
|
||||
@@ -150,7 +150,7 @@
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent: 240 5% 93%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
@@ -183,7 +183,7 @@
|
||||
--muted: 240 5% 16%;
|
||||
--muted-foreground: 240 4% 60%;
|
||||
|
||||
--accent: 240 3.7% 15.9%;
|
||||
--accent: 240 5% 19%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
|
||||
--destructive: 1 100% 69%;
|
||||
|
||||
Reference in New Issue
Block a user