support dark theme closes #25
Deploy service message-delivery-method-web-push / service message-delivery-method-web-push (push) Successful in 1m17s
Deploy db-migrations / db-migrations (push) Successful in 38s
Deploy app frontend / app frontend (push) Successful in 2m14s

This commit is contained in:
2024-12-08 11:06:31 +02:00
parent 0d40aaf2da
commit ab86a3b8e1
34 changed files with 689 additions and 261 deletions
+1
View File
@@ -48,6 +48,7 @@ declare module 'vue' {
RouterView: typeof import('vue-router')['RouterView']
TabBar: typeof import('./src/components/layout/TabBar.vue')['default']
TabMenuItem: typeof import('./src/components/layout/TabMenuItem.vue')['default']
Textarea: typeof import('primevue/textarea')['default']
TheWelcome: typeof import('./src/components/TheWelcome/TheWelcome.vue')['default']
}
}
+1
View File
@@ -63,6 +63,7 @@
"postcss": "^8.4.49",
"prettier": "^3.3.3",
"rollup-plugin-manifest-json": "^1.7.0",
"sass-embedded": "^1.82.0",
"tailwindcss": "^3.4.14",
"tailwindcss-safe-area": "^0.6.0",
"typescript": "~5.6.3",
+16
View File
@@ -29,6 +29,22 @@ registerServiceWorker()
// })
// })
// .catch(console.error)
// import { usePrefersColorSchemeStore } from '@/stores/usePrefersColorSchemeStore'
// import { watch } from 'vue'
// const { isDarkMode } = usePrefersColorSchemeStore()
//
// watch(
// () => isDarkMode,
// () => {
// if (isDarkMode) {
// document.documentElement.classList.add('dark')
// } else {
// document.documentElement.classList.remove('dark')
// }
// },
// { immediate: true },
// )
</script>
<template>
+9 -4
View File
@@ -1,12 +1,17 @@
<template>
<RouterLink :to="route" class="p-4 rounded-lg bg-white shadow-md">
<RouterLink
:to="route"
class="p-4 rounded-lg bg-white dark:bg-gray-800 shadow-md text-gray-800 dark:text-gray-300"
>
<div class="flex items-center justify-between mb-2" v-if="qrCodeDoc">
<div class="flex items-center">
<div class="flex items-center gap-2">
<QRCodePlacementIcon :placement="qrCodeDoc.placement" />
<h2 class="text-lg font-semibold">{{ qrCodeDoc.name }}</h2>
<h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
{{ qrCodeDoc.name }}
</h2>
</div>
</div>
<div class="text-gray-500">
<div class="text-gray-500 dark:text-gray-400">
<span class="text-nowrap">{{ formatedCreatedAt }}</span>
</div>
</RouterLink>
@@ -6,44 +6,53 @@
</div>
<Checkbox v-else v-model="enabled" inputId="webPush" name="delivery" binary />
<label for="webPush">Прислать уведомление, когда придет ответ</label>
<label for="webPush" class="text-gray-800 dark:text-gray-300">
Прислать уведомление, когда придет ответ
</label>
</div>
<!-- Инструкции для iOS -->
<div
v-if="showRequireStandaloneInstructionsForIOS"
class="bg-yellow-100 text-yellow-800 p-4 rounded my-4"
class="bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-300 p-4 rounded my-4"
>
<p class="mb-2">Чтобы включить уведомления, добавьте это приложение на главный экран:</p>
<p class="mb-2 dark:text-yellow-200">
Чтобы включить уведомления, добавьте это приложение на главный экран:
</p>
<ul class="list-disc pl-5">
<li class="mb-1">
<li class="mb-1 dark:text-yellow-200">
Нажмите
<strong class="font-semibold">
"<img
src="@/assets/icons/apple-share.svg?url"
alt="Поделиться"
class="h-7 -mt-2 inline-block"
class="h-7 -mt-2 inline-block dark:filter dark:invert"
/>"
</strong>
внизу экрана
</li>
<li class="mb-1">
<li class="mb-1 dark:text-yellow-200">
Выберите
<strong class="inline-flex items-baseline font-semibold">
<span class="me-1">"На экран «Домой»</span>
<img
src="@/assets/icons/apple-system-plus.svg?url"
alt=""
class="h-4 inline-block self-center"
/>"
class="h-4 inline-block self-center dark:filter dark:invert"
/>
"
</strong>
</li>
<li>Откройте приложение с главного экрана и повторите попытку.</li>
<li class="dark:text-yellow-200">
Откройте приложение с главного экрана и повторите попытку.
</li>
</ul>
</div>
<!-- Ошибка разрешений -->
<p v-else-if="isError" class="text-red-500 mb-2">{{ error?.message || error }}<br /></p>
<p v-else-if="isError" class="text-red-500 dark:text-red-400 mb-2">
{{ error?.message || error }}<br />
</p>
</div>
</template>
@@ -240,6 +249,6 @@ watch(
)
const showRequireStandaloneInstructionsForIOS = computed(
() => isError.value && isIOS.value && !isStandalone.value && enabled.value,
() => true, //isError.value && isIOS.value && !isStandalone.value && enabled.value,
)
</script>
@@ -1,5 +1,5 @@
<template>
<div class="max-w-screen-sm mx-auto px-4 md:px-0 font-sans text-gray-800">
<div class="max-w-screen-sm mx-auto px-4 md:px-0 font-sans text-gray-800 dark:text-gray-100">
<!-- Hero Section -->
<section class="text-center py-12">
<div class="flex flex-col md:flex-row items-center gap-8">
@@ -14,7 +14,7 @@
</RouterLink>
<div>
<h1 class="text-3xl font-bold">Создайте уникальный QRкод для связи</h1>
<p class="mt-2 text-lg text-gray-600 text-justify">
<p class="mt-2 text-lg text-gray-600 dark:text-gray-400 text-justify">
Люди смогут отправлять вам сообщения, сканируя этот QRкод, без необходимости раскрывать
ваши контактные данные.
</p>
@@ -29,12 +29,12 @@
</div>
</section>
<hr />
<hr class="dark:border-gray-700" />
<!-- Where to Use Section -->
<section id="setup" class="py-8 text-center">
<h2 class="text-2xl font-semibold">Где использовать?</h2>
<p class="mt-2 text-lg text-gray-600 text-justify">
<p class="mt-2 text-lg text-gray-600 dark:text-gray-400 text-justify">
QRкод можно будет разместить на вашем автомобиле, на входной двери или на ошейнике вашего
питомца это позволит окружающим легко связаться с вами в случае необходимости, при этом
ваши контактные данные останутся конфиденциальными.
@@ -46,7 +46,7 @@
<!-- Simple Setup Section -->
<section id="settings" class="py-8 text-center">
<h2 class="text-2xl font-semibold">Легкость настройки</h2>
<p class="mt-2 text-lg text-gray-600 text-justify">
<p class="mt-2 text-lg text-gray-600 dark:text-gray-400 text-justify">
Создайте QRкод, выберите доступные действия и начните использовать.
</p>
<div class="flex justify-center mt-6">
@@ -61,7 +61,7 @@
</section>
<!-- Footer -->
<footer class="py-4 border-t text-center">
<footer class="py-4 border-t text-center dark:border-gray-700">
<p>НаСвязи © 2024</p>
</footer>
</div>
@@ -5,10 +5,15 @@
<InputText
id="newMessage"
v-model="newMessage"
class="flex-1"
class="flex-1 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700"
placeholder="Напишите сообщение"
/>
<Button type="submit" icon="pi pi-send" class="p-button-md" :disabled="!newMessage.trim()" />
<Button
type="submit"
icon="pi pi-send"
class="p-button-md dark:bg-primary-600 dark:hover:bg-primary-700"
:disabled="!newMessage.trim()"
/>
</form>
<ChatSubscribeWebPush :qr_code_uri :chat :user_chat_role="userRole" />
@@ -12,8 +12,10 @@
<Card
class="p-card-sm"
:class="{
'bg-blue-50 border-blue-300 text-blue-500': message.from === userRole,
'bg-gray-50 border-gray-300 text-gray-700': message.from !== userRole,
'bg-blue-50 dark:bg-blue-900 border-blue-300 dark:border-blue-700 text-blue-500 dark:text-blue-200':
message.from === userRole,
'bg-gray-50 dark:bg-gray-800 border-gray-300 dark:border-gray-700 text-gray-700 dark:text-gray-300':
message.from !== userRole,
}"
>
<template #content>
@@ -1,12 +1,18 @@
<template>
<section>
<h2 class="text-xl font-semibold mb-4">Выберите доступные действия</h2>
<p class="text-gray-600 mb-4">Укажите, какие действия будут доступны для сканирующих QRкод.</p>
<p class="text-sm text-gray-500 mb-6">Вы сможете изменить эти настройки позже.</p>
<h2 class="text-xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
Выберите доступные действия
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-4">
Укажите, какие действия будут доступны для сканирующих QRкод.
</p>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
Вы сможете изменить эти настройки позже.
</p>
<div class="flex flex-col gap-3 mb-4">
<div v-for="(label, action) in actionsOptions" :key="action" class="flex items-center gap-2">
<Checkbox v-model="model" :inputId="action" :value="action" @change="next" />
<label :for="action">{{ label }}</label>
<label :for="action" class="text-gray-700 dark:text-gray-300">{{ label }}</label>
</div>
</div>
</section>
@@ -1,7 +1,9 @@
<template>
<section>
<h2 class="text-xl font-semibold mb-4">Куда отправлять сообщения?</h2>
<p class="text-gray-600 mb-4">Выберите, как вы хотите получать сообщения.</p>
<h2 class="text-xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
Куда отправлять сообщения?
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-4">Выберите, как вы хотите получать сообщения.</p>
<CreateMessageDeliveryOptions>
<template #telegram="{ label, method }">
<CreateMessageDeliveryOptionTelegram
@@ -9,6 +11,7 @@
:method
v-model="localModel"
@change="onChange"
class="text-gray-700 dark:text-gray-300"
/>
</template>
<template #webPush="{ label, method }">
@@ -17,10 +20,17 @@
:method
v-model="localModel"
@change="onChange"
class="text-gray-700 dark:text-gray-300"
/>
</template>
<template #default="{ label, method }">
<CreateMessageDeliveryOption :label :method v-model="localModel" @change="onChange" />
<CreateMessageDeliveryOption
:label
:method
v-model="localModel"
@change="onChange"
class="text-gray-700 dark:text-gray-300"
/>
</template>
</CreateMessageDeliveryOptions>
</section>
@@ -2,7 +2,7 @@
<CreateMessageDeliveryOption :label :method v-model="model" />
<!-- Сообщение о недоступности -->
<p v-if="telegramNotice" class="text-red-500 mb-2">
<p v-if="telegramNotice" class="text-red-500 dark:text-red-400 my-2 outline outline-1 py-2 px-4">
Отправка в Telegram пока в разработке.<br />
Этот способ отправки сообщений можно будет выбрать позже.
</p>
@@ -4,11 +4,13 @@
<!-- Инструкции для iOS -->
<div
v-if="showRequireStandaloneInstructionsForIOS"
class="bg-yellow-100 text-yellow-800 p-4 rounded my-4"
class="bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-300 p-4 rounded my-4"
>
<p class="mb-2">Чтобы включить уведомления, добавьте это приложение на главный экран:</p>
<p class="mb-2 dark:text-yellow-200">
Чтобы включить уведомления, добавьте это приложение на главный экран:
</p>
<ul class="list-disc pl-5">
<li class="mb-1">
<li class="mb-1 dark:text-yellow-200">
Нажмите
<strong class="font-semibold">
"<img
@@ -19,7 +21,7 @@
</strong>
внизу экрана
</li>
<li class="mb-1">
<li class="mb-1 dark:text-yellow-200">
Выберите
<strong class="inline-flex items-baseline font-semibold">
<span class="me-1">"На экран «Домой»</span>
@@ -30,14 +32,16 @@
/>"
</strong>
</li>
<li>Откройте приложение с главного экрана и повторите попытку.</li>
<li class="dark:text-yellow-200">
Откройте приложение с главного экрана и повторите попытку.
</li>
</ul>
</div>
<!-- Ошибка разрешений -->
<p v-else-if="isError && model === method" class="text-red-500 mb-2">
<p v-else-if="isError && model === method" class="text-red-500 dark:text-red-400 mb-2">
{{ error }}.<br />
<button @click="retry()" class="text-blue-500 underline">Повторить</button>
<button @click="retry()" class="text-blue-500 dark:text-blue-400 underline">Повторить</button>
</p>
</template>
@@ -1,7 +1,11 @@
<template>
<section>
<h2 class="text-xl font-semibold mb-4">Где вы хотите разместить QRкод?</h2>
<p class="text-gray-600 mb-4">Этот выбор поможет настроить QRкод с подходящими действиями.</p>
<h2 class="text-xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
Где вы хотите разместить QRкод?
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-4">
Этот выбор поможет настроить QRкод с подходящими действиями.
</p>
<div class="flex flex-col gap-3 mb-4">
<div v-for="{ label, type } in qrCodePlacements" :key="type" class="flex items-center gap-2">
<RadioButton
@@ -11,7 +15,7 @@
:value="type"
@change="$emit('nextStep')"
/>
<label :for="type">{{ label }}</label>
<label :for="type" class="text-gray-700 dark:text-gray-300">{{ label }}</label>
</div>
</div>
</section>
@@ -1,18 +1,26 @@
<template>
<section>
<h2 class="text-xl font-semibold mb-4">Сообщения для отправки</h2>
<p class="text-gray-600 mb-4">
<h2 class="text-xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
Сообщения для отправки
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-4">
После считывания QRкода можно будет выбрать одно из этих сообщений или написать своё.
</p>
<p class="text-sm text-gray-500 mb-6">Вы сможете изменить эти сообщения позже.</p>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
Вы сможете изменить эти сообщения позже.
</p>
<!-- Список сообщений -->
<div class="flex flex-col gap-2 mb-4">
<div v-for="(message, index) in messages" :key="index" class="flex gap-2" ref="messagesRef">
<InputText v-model="messages[index]" class="flex-1" placeholder="Введите сообщение" />
<InputText
v-model="messages[index]"
class="flex-1 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700"
placeholder="Введите сообщение"
/>
<Button
icon="pi pi-trash"
class="p-button-danger p-button-text"
variant="text"
@click="removeMessage(index)"
title="Удалить сообщение"
/>
@@ -23,7 +31,7 @@
<Button
label="Добавить сообщение"
icon="pi pi-plus"
class="p-button-sm p-button-outlined mb-4"
class="p-button-sm p-button-outlined mb-4 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-300"
@click="addMessage"
/>
</section>
@@ -1,17 +1,28 @@
<template>
<section>
<h2 class="text-xl font-semibold mb-4">Введите название для вашего QRкода</h2>
<p class="text-gray-600 mb-4">Название поможет вам организовать ваши QRкоды.</p>
<h2 class="text-xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
Введите название для вашего QRкода
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-4">
Название поможет вам организовать ваши QRкоды.
</p>
<form @submit.prevent="emit('nextStep')">
<label for="name" class="block text-gray-700 font-semibold mb-2">Название</label>
<InputText id="name" v-model="name" class="w-full" placeholder="Введите название" />
<p v-if="!name" class="text-red-500 text-sm mt-1">Название обязательно.</p>
<label for="name" class="block text-gray-700 dark:text-gray-300 font-semibold mb-2">
Название
</label>
<InputText
id="name"
v-model="name"
class="w-full dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700"
placeholder="Введите название"
/>
<p v-if="!name" class="text-red-500 dark:text-red-400 text-sm mt-1">Название обязательно</p>
<Button
type="submit"
:label="buttonLabel"
class="p-button-md mt-4 mb-4"
class="p-button-md mt-4 mb-4 dark:border-orange-500 dark:hover:border-orange-600 dark:text-gray-300"
:loading="isPending"
:disabled="!name || isPending"
/>
@@ -1,9 +1,9 @@
<template>
<div
class="sticky bottom-0 bg-white shadow-md py-safe-or-4 sm:border-b sm:order-first sm:justify-center touch-none"
class="sticky bottom-0 bg-white dark:bg-gray-800 shadow-md dark:shadow-lg py-safe-or-4 sm:border-b dark:sm:border-gray-700 sm:order-first sm:justify-center touch-none"
>
<div
class="flex justify-around sm:justify-between sm:gap-8 sm:px-4 sm:flex-row sm:bg-white sm:max-w-prose mx-auto"
class="flex justify-around sm:justify-between sm:gap-8 sm:px-4 sm:flex-row sm:bg-white dark:sm:bg-gray-800 sm:max-w-prose mx-auto"
>
<TabMenuItem
v-for="{ route, label, icon, unreadCount } in tabs"
@@ -12,6 +12,7 @@
:icon
:to="{ name: route }"
:unreadCount
class="dark:text-gray-300 dark:hover:text-gray-100"
/>
</div>
</div>
@@ -6,16 +6,26 @@
@click="navigate"
class="flex flex-col items-center w-full sm:flex-row sm:items-center sm:w-auto gap-1 relative"
>
<i :class="['pi', `pi-${icon}`, 'text-2xl sm:mr-2', isActive && 'text-primary-700']"></i>
<i
:class="[
'pi',
`pi-${icon}`,
'text-2xl sm:mr-2',
isActive ? 'text-primary-700 dark:text-primary-300' : 'text-gray-700 dark:text-gray-300',
]"
></i>
<span
class="text-xs font-medium sm:text-base text-center"
:class="{ 'text-primary-700': isActive, 'text-gray-700': !isActive }"
:class="{
'text-primary-700 dark:text-primary-300': isActive,
'text-gray-700 dark:text-gray-300': !isActive,
}"
>
{{ label }}
</span>
<span
v-if="unreadCount && unreadCount > 0"
class="absolute top-0 -right-2.5 bg-red-500 text-white text-xs rounded-full px-1"
class="absolute top-0 -right-2.5 bg-red-500 dark:bg-red-600 text-white text-xs rounded-full px-1"
>
{{ unreadCount }}
</span>
@@ -1,12 +1,24 @@
<template>
<a
:href="url"
class="font-mono text-nowrap text-sm text-gray-600 underline underline-offset-4 hover:text-gray-800 hover:no-underline"
class="font-mono text-nowrap text-sm hover:no-underline"
target="_blank"
>{{ url }}</a
>
v-text="url"
/>
</template>
<style scoped lang="scss">
a {
color: var(--p-surface-500); // Очень нейтральный цвет для менее акцентной ссылки
text-decoration: none;
&:hover {
color: var(--p-surface-700); // Немного более насыщенный цвет при наведении
text-decoration: none;
}
}
</style>
<script setup lang="ts">
defineProps<{ url: string }>()
</script>
@@ -1,11 +1,13 @@
<template>
<div class="max-w-3xl mx-auto py-6 px-4 md:px-6">
<div v-if="isLoading" class="text-gray-500">Загрузка инструкции...</div>
<div v-else-if="data" class="markdown">
<div v-if="isLoading" class="text-gray-500 dark:text-gray-400">Загрузка инструкции...</div>
<div v-else-if="data" class="markdown dark:text-gray-300">
<VueMarkdownIt :source="data" />
</div>
<p v-else-if="isError" class="text-red-500">Ошибка загрузки инструкции.</p>
<p v-else class="text-gray-500">Инструкция для этого типа размещения отсутствует.</p>
<p v-else-if="isError" class="text-red-500 dark:text-red-400">Ошибка загрузки инструкции.</p>
<p v-else class="text-gray-500 dark:text-gray-400">
Инструкция для этого типа размещения отсутствует.
</p>
</div>
</template>
@@ -35,102 +37,97 @@ const { isLoading, isError, data } = useQuery({
})
</script>
<style scoped>
<style scoped lang="scss">
.markdown {
font-family: 'Inter', sans-serif;
line-height: 1.7;
color: #333;
}
color: var(--p-text-color);
.markdown ::v-deep(h1) {
& ::v-deep(h1) {
font-size: 2rem;
margin-bottom: 1rem;
color: #111827;
color: var(--p-primary-color);
font-weight: 700;
}
}
.markdown ::v-deep(h2) {
& ::v-deep(h2) {
font-size: 1.75rem;
margin-top: 1.5rem;
margin-bottom: 1rem;
color: #1f2937;
color: var(--p-secondary-color);
font-weight: 600;
}
}
.markdown ::v-deep(h3) {
& ::v-deep(h3) {
font-size: 1.5rem;
margin-top: 1.25rem;
margin-bottom: 0.75rem;
color: #374151;
color: var(--p-secondary-light-color);
font-weight: 500;
}
}
.markdown ::v-deep(p) {
& ::v-deep(p) {
margin-bottom: 1rem;
color: #4b5563;
}
color: var(--p-text-secondary-color);
}
.markdown ::v-deep(ul) {
list-style: disc;
& ::v-deep(ul),
& ::v-deep(ol) {
margin-left: 1.5rem;
margin-bottom: 1rem;
}
}
.markdown ::v-deep(ol) {
list-style: decimal;
margin-left: 1.5rem;
margin-bottom: 1rem;
}
.markdown ::v-deep(li) {
& ::v-deep(li) {
margin-bottom: 0.5rem;
}
}
.markdown ::v-deep(a) {
color: #2563eb;
& ::v-deep(a) {
color: var(--p-link-color);
text-decoration: underline;
}
}
.markdown ::v-deep(blockquote) {
border-left: 4px solid #d1d5db;
& ::v-deep(blockquote) {
border-left: 4px solid var(--p-border-color);
padding-left: 1rem;
color: #6b7280;
color: var(--p-text-secondary-color);
margin: 1rem 0;
font-style: italic;
}
}
.markdown ::v-deep(code) {
background-color: #f3f4f6;
& ::v-deep(code) {
background-color: var(--p-surface-50);
border-radius: 4px;
padding: 0.2rem 0.4rem;
font-family: 'Courier New', Courier, monospace;
color: #1f2937;
}
color: var(--p-text-color);
}
.markdown ::v-deep(pre) {
background-color: #f9fafb;
& ::v-deep(pre) {
background-color: var(--p-surface-100);
padding: 1rem;
border-radius: 8px;
overflow-x: auto;
color: #111827;
color: var(--p-text-color);
font-family: 'Courier New', Courier, monospace;
}
}
.markdown ::v-deep(table) {
& ::v-deep(table) {
width: 100%;
border-collapse: collapse;
margin-bottom: 1rem;
}
}
.markdown ::v-deep(th),
.markdown ::v-deep(td) {
border: 1px solid #d1d5db;
& ::v-deep(th),
& ::v-deep(td) {
border: 1px solid var(--p-border-color);
padding: 0.5rem;
}
}
.markdown ::v-deep(th) {
background-color: #f9fafb;
& ::v-deep(th) {
background-color: var(--p-surface-200);
font-weight: 600;
text-align: left;
color: var(--p-text-color);
}
}
</style>
@@ -1,18 +1,17 @@
<template>
<RouterLink :to="`/manage/qr/${qrCode.uri}`" class="p-4 rounded-lg bg-white shadow-md">
<RouterLink
:to="`/manage/qr/${qrCode.uri}`"
class="p-4 rounded-lg bg-white dark:bg-gray-800 shadow-md text-gray-800 dark:text-gray-300"
>
<div class="flex items-center justify-between mb-2">
<div class="flex items-center">
<div class="flex items-center gap-2">
<QRCodePlacementIcon :placement="qrCode.placement" />
<h2 class="text-lg font-semibold">{{ qrCode.name }}</h2>
<h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100">
{{ qrCode.name }}
</h2>
</div>
<!-- <Button-->
<!-- label="Настроить"-->
<!-- :to="`/manage/${qrCode.uri}/settings`"-->
<!-- as="router-link"-->
<!-- class="p-button-outlined p-button-sm"-->
<!-- />-->
</div>
<div class="text-gray-500">
<div class="text-gray-500 dark:text-gray-400">
Создан: <span class="text-nowrap">{{ formatedCreatedAt }}</span>
</div>
</RouterLink>
@@ -1,5 +1,5 @@
<template>
<i :class="['pi', iconClass, 'text-gray-500', 'mr-2']" />
<i :class="['pi', iconClass, 'text-gray-500', 'dark:text-gray-300', 'mr-2']" />
</template>
<script lang="ts" setup>
@@ -1,7 +1,9 @@
<template>
<section class="max-w-prose w-full text-center">
<!-- Контактная информация -->
<div class="flex items-start bg-gray-100 border rounded-lg p-4">
<div
class="flex items-start bg-gray-100 dark:bg-gray-800 border dark:border-gray-700 rounded-lg p-4"
>
<!-- Аватарка Gravatar -->
<div v-if="contactInfo.email" class="flex-shrink-0 mr-4">
<img
@@ -14,18 +16,23 @@
<!-- Контакты -->
<div class="text-left flex-grow">
<p v-if="contactInfo.name" class="text-lg font-semibold">{{ contactInfo.name }}</p>
<p v-if="contactInfo.name" class="text-lg font-semibold text-gray-900 dark:text-gray-100">
{{ contactInfo.name }}
</p>
<p v-if="contactInfo.phone" class="text-gray-700">
<p v-if="contactInfo.phone" class="text-gray-700 dark:text-gray-300">
Телефон:
<a :href="`tel:${formattedPhone}`" class="underline text-blue-600">
<a :href="`tel:${formattedPhone}`" class="underline text-blue-600 dark:text-blue-400">
{{ contactInfo.phone }}
</a>
</p>
<p v-if="contactInfo.email" class="text-gray-700">
<p v-if="contactInfo.email" class="text-gray-700 dark:text-gray-300">
Email:
<a :href="`mailto:${contactInfo.email}`" class="underline text-blue-600">
<a
:href="`mailto:${contactInfo.email}`"
class="underline text-blue-600 dark:text-blue-400"
>
{{ contactInfo.email }}
</a>
</p>
@@ -40,8 +47,12 @@
</div>
</div>
<p v-if="success" class="text-green-500 mt-4">Контакт успешно добавлен в адресную книгу!</p>
<p v-if="error" class="text-red-500 mt-4">Ошибка добавления контакта: {{ error }}</p>
<p v-if="success" class="text-green-500 dark:text-green-400 mt-4">
Контакт успешно добавлен в адресную книгу!
</p>
<p v-if="error" class="text-red-500 dark:text-red-400 mt-4">
Ошибка добавления контакта: {{ error }}
</p>
</section>
</template>
@@ -1,6 +1,9 @@
<template>
<section class="max-w-prose w-full">
<p class="text-gray-600 mb-4 text-center" v-if="doc.predefinedMessages?.length">
<p
class="text-gray-600 dark:text-gray-400 mb-4 text-center"
v-if="doc.predefinedMessages?.length"
>
Выберите сообщение из списка или напишите своё:
</p>
@@ -10,7 +13,7 @@
<Button
v-for="(message, index) in doc.predefinedMessages"
:key="index"
class="p-button-outlined w-full text-left"
class="w-full p-button-outlined text-left dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700"
:disabled="loading"
@click="sendPresetMessage(message)"
>
@@ -24,20 +27,22 @@
<InputText
id="customMessage"
v-model="customMessage"
class="flex-1"
class="flex-1 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700"
placeholder="Напишите сообщение"
:disabled="loading"
/>
<Button
type="submit"
icon="pi pi-send"
class="p-button-md"
class="p-button-md dark:bg-primary-600 dark:hover:bg-primary-700"
:disabled="!customMessage.trim() || loading"
/>
</form>
<p v-if="success" class="text-green-500 mt-4 text-center">Сообщение отправлено!</p>
<p v-if="error" class="text-red-500 mt-4 text-center">Ошибка: {{ error }}</p>
<p v-if="success" class="text-green-500 dark:text-green-400 mt-4 text-center">
Сообщение отправлено!
</p>
<p v-if="error" class="text-red-500 dark:text-red-400 mt-4 text-center">Ошибка: {{ error }}</p>
</section>
</template>
+10 -2
View File
@@ -2,6 +2,7 @@
<div
class="grid h-full sm:grid-rows-[auto_1fr_auto] grid-rows-[1fr_auto_auto] overflow-hidden touch-none"
>
<!-- Content Section -->
<div
class="flex overflow-hidden overflow-y-auto pb-safe-or-4 sm:pt-safe px-4 overscroll-contain touch-manipulation"
ref="contentScrollContainer"
@@ -11,20 +12,27 @@
</div>
</div>
<!-- Footer Section -->
<slot name="footer">
<footer
v-if="!slots.footer"
class="bg-gray-100 text-center py-1.5 border-t text-xs sm:text-base text-gray-700 sm:order-last sm:pb-safe-or-1.5"
class="bg-gray-100 dark:bg-gray-800 text-center py-1.5 border-t dark:border-gray-700 text-xs sm:text-base text-gray-700 dark:text-gray-300 sm:order-last sm:pb-safe-or-1.5"
>
<span class="text-nowrap">Сделайте НаСвязи удобнее.</span>
<span> </span>
<span class="text-nowrap">
Будем рады
<a href="/donate" class="underline underline-offset-2">вашей поддержке</a>
<a
href="/donate"
class="underline underline-offset-2 text-primary-600 dark:text-primary-400"
>
вашей поддержке
</a>
</span>
</footer>
</slot>
<!-- TabBar -->
<TabBar />
</div>
</template>
+3 -22
View File
@@ -6,29 +6,10 @@ import { VueQueryPlugin, type VueQueryPluginOptions } from '@tanstack/vue-query'
import PouchDB from '@/api/PouchDB'
import PrimeVue from 'primevue/config'
import { definePreset } from '@primevue/themes'
import Aura from '@primevue/themes/aura'
import { PrimeVueHereConnectPreset } from './themes/PrimeVueHereConnectPreset'
import './style.css'
import 'primeicons/primeicons.css'
const MyPreset = definePreset(Aura, {
semantic: {
primary: {
50: '{rose.50}',
100: '{rose.100}',
200: '{rose.200}',
300: '{rose.300}',
400: '{rose.400}',
500: '{rose.500}',
600: '{rose.600}',
700: '{rose.700}',
800: '{rose.800}',
900: '{rose.900}',
950: '{rose.950}',
},
},
})
import App from './App.vue'
import router from './router'
@@ -55,9 +36,9 @@ app.use(VueQueryPlugin, vueQueryPluginOptions)
app.use(router)
app.use(PrimeVue, {
theme: {
preset: MyPreset,
preset: PrimeVueHereConnectPreset,
options: {
darkModeSelector: false,
darkModeSelector: 'system',
},
},
})
@@ -0,0 +1,13 @@
import { ref, watch } from 'vue'
import { defineStore } from 'pinia'
export const usePrefersColorSchemeStore = defineStore('prefersColorScheme', () => {
const prefersColorSchemeDarkMatchMedia = window.matchMedia('(prefers-color-scheme: dark)')
const isDarkMode = ref<boolean>(prefersColorSchemeDarkMatchMedia.matches)
prefersColorSchemeDarkMatchMedia.addEventListener('change', (e) => {
isDarkMode.value = e.matches
})
return { isDarkMode }
})
@@ -0,0 +1,45 @@
import { definePreset } from '@primevue/themes'
import Aura from '@primevue/themes/aura'
export const PrimeVueHereConnectPreset = definePreset(Aura, {
semantic: {
colorScheme: {
light: {
primary: {
50: '{rose.50}',
100: '{rose.100}',
200: '{rose.200}',
300: '{rose.300}',
400: '{rose.400}',
500: '{rose.500}',
600: '{rose.600}',
700: '{rose.700}',
800: '{rose.800}',
900: '{rose.900}',
950: '{rose.950}',
},
highlight: {
background: '{rose.100}',
focusBackground: '{rose.200}',
color: '#000000',
focusColor: '#000000',
},
},
dark: {
primary: {
50: '{orange.50}',
100: '{orange.100}',
200: '{orange.200}',
300: '{orange.300}',
400: '{orange.400}',
500: '{orange.500}',
600: '{orange.600}',
700: '{yellow.700}',
800: '{orange.800}',
900: '{orange.900}',
950: '{orange.950}',
},
},
},
},
})
+3 -1
View File
@@ -39,7 +39,9 @@
</template>
</CreateSectionManager>
<p v-if="isError" class="text-red-500 mt-4">Ошибка создания QRкода: {{ error }}</p>
<p v-if="isError" class="text-red-500 dark:text-red-400 mt-4">
Ошибка создания QRкода: {{ error }}
</p>
<div class="h-[30dvh] md:h-[10dvh] overflow-hidden"></div>
</LayoutWithTabs>
+12 -31
View File
@@ -1,7 +1,9 @@
<template>
<LayoutWithTabs>
<template #footer>
<footer class="py-4 border-t text-center text-sm hidden sm:block">
<footer
class="py-4 border-t text-center text-sm hidden sm:block border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400"
>
<p>НаСвязи © 2024</p>
</footer>
</template>
@@ -12,8 +14,10 @@
</MetaHead>
<section class="max-w-2xl mx-auto py-8 px-4 text-center">
<h1 class="text-2xl font-bold mb-4">Поддержите развитие НаСвязи</h1>
<p class="text-gray-600 mb-6">
<h1 class="text-2xl font-bold mb-4 text-gray-900 dark:text-gray-100">
Поддержите развитие НаСвязи
</h1>
<p class="text-gray-600 dark:text-gray-400 mb-6">
Мы делаем всё, чтобы НаСвязи оставался удобным и полезным. Вы можете помочь нам, поддержав
проект на Бусти.
</p>
@@ -24,7 +28,7 @@
href="https://boosty.to/hereconnect/donate"
target="_blank"
rel="noopener noreferrer"
class="inline-block bg-orange-500 text-white font-semibold py-3 px-6 rounded-lg shadow hover:bg-orange-600 transition duration-300"
class="inline-block bg-orange-500 text-white font-semibold py-3 px-6 rounded-lg shadow hover:bg-orange-600 transition duration-300 dark:hover:bg-orange-700"
>
Поддержать через Бусти
</a>
@@ -32,8 +36,10 @@
<!-- Рассказать друзьям -->
<div class="mb-8">
<h2 class="text-xl font-semibold mb-4">Рассказать друзьям</h2>
<p class="text-gray-600 mb-4">Поделитесь ссылкой на НаСвязи:</p>
<h2 class="text-xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
Рассказать друзьям
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-4">Поделитесь ссылкой на НаСвязи:</p>
<div class="flex justify-center gap-6">
<!-- Telegram -->
<a
@@ -56,31 +62,6 @@
</a>
</div>
</div>
<!-- Обратная связь -->
<!-- <div>-->
<!-- <h2 class="text-xl font-semibold mb-4">Оставить отзыв или идею</h2>-->
<!-- <p class="text-gray-600 mb-4">Мы рады любым предложениям и обратной связи.</p>-->
<!-- <form @submit.prevent="sendFeedback">-->
<!-- <div class="mb-4">-->
<!-- <InputText v-model="email" class="w-full" placeholder="Ваш email" />-->
<!-- </div>-->
<!-- <div class="mb-4">-->
<!-- <Textarea-->
<!-- v-model="message"-->
<!-- rows="4"-->
<!-- class="w-full"-->
<!-- placeholder="Ваше сообщение"-->
<!-- ></Textarea>-->
<!-- </div>-->
<!-- <Button-->
<!-- label="Отправить"-->
<!-- class="p-button-md"-->
<!-- :disabled="!email.trim() || !message.trim()"-->
<!-- />-->
<!-- </form>-->
<!-- <p v-if="success" class="text-green-500 mt-4">Спасибо за ваше сообщение!</p>-->
<!-- </div>-->
</section>
</LayoutWithTabs>
</template>
+5 -3
View File
@@ -19,10 +19,12 @@
<!-- Уведомление, если гость отправил только одно сообщение -->
<div
v-if="count === 1 && lastMessage && lastMessage.from === 'guest'"
class="flex flex-col items-center justify-center text-center bg-gray-50 border border-gray-200 rounded-lg shadow-md p-6 my-6"
class="flex flex-col items-center justify-center text-center bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-md p-6 my-6"
>
<p class="text-gray-700 font-medium text-lg">Ваше сообщение отправлено.</p>
<p class="text-gray-600 mt-2">
<p class="text-gray-700 dark:text-gray-200 font-medium text-lg">
Ваше сообщение отправлено.
</p>
<p class="text-gray-600 dark:text-gray-400 mt-2">
Вы можете написать ещё одно сообщение или подождать ответа.
</p>
</div>
+9 -3
View File
@@ -22,15 +22,21 @@
</section>
</main>
<footer class="bg-gray-100 text-center py-4 border-t text-sm text-gray-700">
<footer
class="bg-gray-100 dark:bg-gray-800 text-center py-4 border-t dark:border-gray-700 text-sm text-gray-700 dark:text-gray-300"
>
<p>
Нужен такой же?
<a href="/create" class="underline underline-offset-2">Создайте свой QRкод</a>.
<a
href="/create"
class="underline underline-offset-2 text-primary-600 dark:text-primary-400"
>Создайте свой QRкод</a
>.
</p>
<p class="mt-1">
Сделайте НаСвязи удобнее. Будем рады вашей&nbsp;<a
href="/donate"
class="underline underline-offset-2"
class="underline underline-offset-2 text-primary-600 dark:text-primary-400"
>поддержке</a
>.
</p>
@@ -18,24 +18,28 @@
<section class="max-w-prose w-full flex flex-col gap-1">
<div
v-if="svgQueryIsLoading || qrCodeQueryIsLoading"
class="text-gray-500 mt-4 text-center"
class="text-gray-500 dark:text-gray-400 mt-4 text-center"
>
<ProgressSpinner styleClass="mt-4" />
</div>
<p v-else-if="svgQueryIsError" class="text-red-500 mt-4 text-center">
<p v-else-if="svgQueryIsError" class="text-red-500 dark:text-red-400 mt-4 text-center">
Ошибка генерации QRкода: {{ svgQueryError?.message }}
</p>
<p v-else-if="qrCodeQueryIsError" class="text-red-500 mt-4 text-center">
<p v-else-if="qrCodeQueryIsError" class="text-red-500 dark:text-red-400 mt-4 text-center">
Ошибка загрузки данных QRкода: {{ qrCodeQueryError!.message }}
</p>
<template v-else>
<h2 class="text-xl font-semibold mb-4">Ваш QRкод готов</h2>
<p class="text-gray-600 mb-4">
<h2 class="text-xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
Ваш QRкод готов
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-4">
Сохраните его и ознакомьтесь с инструкциями по размещению.
</p>
<!-- Рендер QRкода -->
<div class="border p-4 bg-white rounded-lg mb-6">
<div class="border dark:border-gray-700 p-4 bg-white rounded-lg mb-6">
<!-- dark:bg-gray-800 -->
<img v-if="!svgQueryIsLoading && svgQueryData" :src="svgQueryData" alt="QR‑код" />
</div>
@@ -47,7 +51,7 @@
<Button
label="Скачать SVG"
icon="pi pi-download"
class="p-button-sm"
class="p-button-sm dark:border-orange-500 dark:hover:border-orange-600 dark:text-gray-300"
:href="svgQueryData"
:download="`${sanitizedFileName}.svg`"
as="a"
+12 -1
View File
@@ -6,7 +6,18 @@ import tailwindcssSafeArea from 'tailwindcss-safe-area'
export default {
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
theme: {
extend: {},
extend: {
colors: {
// primary: {
// 400: '#FFA726', // Светлый акцентный цвет для темной темы
// 600: '#FB8C00', // Основной акцентный цвет для светлой темы
// },
// orange: {
// 600: '#FB923C',
// 700: '#F97316',
// },
},
},
},
plugins: [tailwindScrollbar, tailwindcssPrimeUI, tailwindcssSafeArea],
}
+276 -18
View File
@@ -143,6 +143,9 @@ importers:
rollup-plugin-manifest-json:
specifier: ^1.7.0
version: 1.7.0
sass-embedded:
specifier: ^1.82.0
version: 1.82.0
tailwindcss:
specifier: ^3.4.14
version: 3.4.15
@@ -163,7 +166,7 @@ importers:
version: 0.27.5(vue@3.5.13)
vite:
specifier: ^5.4.10
version: 5.4.11(@types/node@20.17.9)
version: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
vite-plugin-mock-data:
specifier: ^4.0.6
version: 4.0.6
@@ -172,7 +175,7 @@ importers:
version: 7.6.7(vite@5.4.11)(vue@3.5.13)
vitest:
specifier: ^2.1.4
version: 2.1.6(@types/node@20.17.9)(jsdom@25.0.1)
version: 2.1.6(@types/node@20.17.9)(jsdom@25.0.1)(sass-embedded@1.82.0)
vue-tsc:
specifier: ^2.1.10
version: 2.1.10(typescript@5.6.3)
@@ -717,6 +720,10 @@ packages:
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
dev: true
/@bufbuild/protobuf@2.2.2:
resolution: {integrity: sha512-UNtPCbrwrenpmrXuRwn9jYpPoweNXj8X5sMvYgsqYyaH8jQ6LfUJSk3dJLnBK+6sfYPrF4iAIo5sd5HQ+tg75A==}
dev: true
/@esbuild/aix-ppc64@0.21.5:
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
engines: {node: '>=12'}
@@ -2049,7 +2056,7 @@ packages:
vite: ^5.0.0 || ^6.0.0
vue: ^3.2.25
dependencies:
vite: 5.4.11(@types/node@20.17.9)
vite: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
vue: 3.5.13(typescript@5.6.3)
dev: true
@@ -2069,7 +2076,7 @@ packages:
'@typescript-eslint/utils': 8.16.0(eslint@9.15.0)(typescript@5.6.3)
eslint: 9.15.0
typescript: 5.6.3
vitest: 2.1.6(@types/node@20.17.9)(jsdom@25.0.1)
vitest: 2.1.6(@types/node@20.17.9)(jsdom@25.0.1)(sass-embedded@1.82.0)
dev: true
/@vitest/expect@0.34.6:
@@ -2103,7 +2110,7 @@ packages:
'@vitest/spy': 2.1.6
estree-walker: 3.0.3
magic-string: 0.30.14
vite: 5.4.11(@types/node@20.17.9)
vite: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
dev: true
/@vitest/pretty-format@2.1.6:
@@ -2684,6 +2691,10 @@ packages:
node-int64: 0.4.0
dev: true
/buffer-builder@0.2.0:
resolution: {integrity: sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==}
dev: true
/buffer-equal-constant-time@1.0.1:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
dev: false
@@ -2833,6 +2844,10 @@ packages:
/color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
/colorjs.io@0.5.2:
resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==}
dev: true
/combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
@@ -3959,6 +3974,10 @@ packages:
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
dev: false
/immutable@5.0.3:
resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==}
dev: true
/import-fresh@3.3.0:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
engines: {node: '>=6'}
@@ -5808,6 +5827,12 @@ packages:
dependencies:
queue-microtask: 1.2.3
/rxjs@7.8.1:
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
dependencies:
tslib: 2.8.1
dev: true
/safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
dev: false
@@ -5821,6 +5846,222 @@ packages:
/safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
/sass-embedded-android-arm64@1.82.0:
resolution: {integrity: sha512-bldHMs02QQWXsgHUZRgolNnZdMjN6XHvmUYoRkzmFq7lsvtLU6SJg2S1Wa9IZJs9jRWdTmOgA6YibSf3pROyFQ==}
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/sass-embedded-android-arm@1.82.0:
resolution: {integrity: sha512-ttGMvWnA/5TYdZTjr5fWHDbb9nZgKipHKCc9zZQRF5HjUydOYWKNqmAJHQtbFWaq35kd5qn6yiE73IJN6eJ6wA==}
engines: {node: '>=14.0.0'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/sass-embedded-android-ia32@1.82.0:
resolution: {integrity: sha512-FUJOnxw8IYKuYuxxiOkk6QXle8/yQFtKjnuSAJuZ5ZpLVMcSZzLc3SWOtuEXYx5iSAfJCO075o2ZoG/pPrJ9aw==}
engines: {node: '>=14.0.0'}
cpu: [ia32]
os: [android]
requiresBuild: true
dev: true
optional: true
/sass-embedded-android-riscv64@1.82.0:
resolution: {integrity: sha512-rd+vc+sxJxNnbhaubiIJmnb1b3FvC9wxCIq8spstopbO7o1uufvBBDeRoFSJaN+7oNhamzjlYGdu6aQoQNs3+A==}
engines: {node: '>=14.0.0'}
cpu: [riscv64]
os: [android]
requiresBuild: true
dev: true
optional: true
/sass-embedded-android-x64@1.82.0:
resolution: {integrity: sha512-EVlybGTgJ8wNLyWj8RUatPXSnmIcvCsx3EfsRfBfhGihLbn4NNpavYO9QsvZzI2XWbJqHLBCd+CvkTcDw/TaSQ==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/sass-embedded-darwin-arm64@1.82.0:
resolution: {integrity: sha512-LvdJPojjKlNGYOB0nSUR/ZtMDuAF4puspHlwK42aA/qK292bfSkMUKZPPapB2aSRwccc/ieBq5fI7n/WHrOCVw==}
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/sass-embedded-darwin-x64@1.82.0:
resolution: {integrity: sha512-6LfnD6YmG1aBfd3ReqMOJDb6Pg2Z/hmlJB7nU+Lb3E+hCNjAZAgeUHQxU/Pm1eIqJJTU/h4ib5QP0Pt9O8yVnw==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/sass-embedded-linux-arm64@1.82.0:
resolution: {integrity: sha512-590/y0HJr/JiyxaqgR7Xf9P20BIhJ+zhB/afAnVuZe/4lEfCpTyM5xMe2+sKLsqtrVyzs9Zm/M4S4ASUOPCggA==}
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/sass-embedded-linux-arm@1.82.0:
resolution: {integrity: sha512-ozjdC5rWzyi5Vo300I4tVZzneXOTQUiaxOr7DjtN26HuFaGAGCGmvThh2BRV4RvySg++5H9rdFu+VgyUQ5iukw==}
engines: {node: '>=14.0.0'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/sass-embedded-linux-ia32@1.82.0:
resolution: {integrity: sha512-hpc4acZ3UTjjJ3Q/GUXqQOCSml6AFKaku0HMawra9bKyRmOpxn8V5hqgXeOWVjK2oQzCmCnJvwKoQUP+S/SIYQ==}
engines: {node: '>=14.0.0'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/sass-embedded-linux-musl-arm64@1.82.0:
resolution: {integrity: sha512-bc2MUSMv/jabnNGEyKP2jQAYZoEzTT/c633W6QoeSEWETGCuTNjaHvWWE6qSI6/UfRg1EpuV1LQA2jPMzZfv/w==}
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/sass-embedded-linux-musl-arm@1.82.0:
resolution: {integrity: sha512-R5PQmY/I+GSoMtfLo8GgHkvF/q6x6y8VNM7yu/Ac1mJj86n48VFi29W1HfY2496+Q6cpAq7toobDj7YfldIdVA==}
engines: {node: '>=14.0.0'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/sass-embedded-linux-musl-ia32@1.82.0:
resolution: {integrity: sha512-ZQKCFKm5TBcJ19UG6uUQmIKfVCJIWMb7e1a93lGeujSb9gyKF5Fb6MN3tuExoT7iFK8zU0Z9iyHqh93F58lcCw==}
engines: {node: '>=14.0.0'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/sass-embedded-linux-musl-riscv64@1.82.0:
resolution: {integrity: sha512-5meSU8BHFeaT09RWfkuUrikRlC+WZcYb9To7MpfV1d9nlD7CZ2xydPExK+mj3DqRuQvTbvhMPcr7f+pHlgHINQ==}
engines: {node: '>=14.0.0'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/sass-embedded-linux-musl-x64@1.82.0:
resolution: {integrity: sha512-ASLAMfjWv7YEPBvEOVlb3zzHq8l4Y9Eh4x3m7B1dNauGVbO11Yng5cPCX/XbwGVf30BtE75pwqvV7oXxBtN15w==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/sass-embedded-linux-riscv64@1.82.0:
resolution: {integrity: sha512-qWvRDXCXH3GzD8OcP0ntd8gBTK3kZyUeyXmxQDZyEtMAM4STC2Tn7+5+2JYYHlppzqWnZPFBNESvpKeOtHaBBw==}
engines: {node: '>=14.0.0'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/sass-embedded-linux-x64@1.82.0:
resolution: {integrity: sha512-AmRaHqShztwfep+M4NagdGaY7fTyWGSOM3k4Z/dd7q4nZclXbALLqNJtKx8xOM7A41LHYJ9zDpIBVRkrh0PzTA==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/sass-embedded-win32-arm64@1.82.0:
resolution: {integrity: sha512-zL9JDQZHXHSGAZe5DqSrR86wMHbm9QPziU4/3hoIG+99StuS74CuV42+hw/+FXXBkXMWbjKWsyF/HZt+I/wJuw==}
engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/sass-embedded-win32-ia32@1.82.0:
resolution: {integrity: sha512-xE+AzLquCkFPnnpo0NHjQdLRIhG1bVs42xIKx42aUbVLYKkBDvbBGpw6EtTscRMyvcjoOqGH5saRvSFComUQcw==}
engines: {node: '>=14.0.0'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/sass-embedded-win32-x64@1.82.0:
resolution: {integrity: sha512-cEgfOQG5womOzzk16ReTv2dxPq5BG16LgLUold/LH9IZH86u4E/MN7Fspf4RWeEJ2EcLdew9QYSC2YWs1l98dQ==}
engines: {node: '>=14.0.0'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/sass-embedded@1.82.0:
resolution: {integrity: sha512-v13sRVVZtWAQLpAGTz5D8hy+oyNKRHao5tKVc/P6AMqSP+jDM8X6GkEpL0jfbu3MaN2/hAQsd4Qx14GG1u0prQ==}
engines: {node: '>=16.0.0'}
hasBin: true
dependencies:
'@bufbuild/protobuf': 2.2.2
buffer-builder: 0.2.0
colorjs.io: 0.5.2
immutable: 5.0.3
rxjs: 7.8.1
supports-color: 8.1.1
sync-child-process: 1.0.2
varint: 6.0.0
optionalDependencies:
sass-embedded-android-arm: 1.82.0
sass-embedded-android-arm64: 1.82.0
sass-embedded-android-ia32: 1.82.0
sass-embedded-android-riscv64: 1.82.0
sass-embedded-android-x64: 1.82.0
sass-embedded-darwin-arm64: 1.82.0
sass-embedded-darwin-x64: 1.82.0
sass-embedded-linux-arm: 1.82.0
sass-embedded-linux-arm64: 1.82.0
sass-embedded-linux-ia32: 1.82.0
sass-embedded-linux-musl-arm: 1.82.0
sass-embedded-linux-musl-arm64: 1.82.0
sass-embedded-linux-musl-ia32: 1.82.0
sass-embedded-linux-musl-riscv64: 1.82.0
sass-embedded-linux-musl-x64: 1.82.0
sass-embedded-linux-riscv64: 1.82.0
sass-embedded-linux-x64: 1.82.0
sass-embedded-win32-arm64: 1.82.0
sass-embedded-win32-ia32: 1.82.0
sass-embedded-win32-x64: 1.82.0
dev: true
/saxes@6.0.0:
resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
engines: {node: '>=v12.22.7'}
@@ -6092,6 +6333,18 @@ packages:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
dev: true
/sync-child-process@1.0.2:
resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==}
engines: {node: '>=16.0.0'}
dependencies:
sync-message-port: 1.1.3
dev: true
/sync-message-port@1.1.3:
resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==}
engines: {node: '>=16.0.0'}
dev: true
/synckit@0.9.2:
resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -6494,7 +6747,7 @@ packages:
git-url-parse: 15.0.0
simple-git: 3.27.0
unplugin: 1.16.0
vite: 5.4.11(@types/node@20.17.9)
vite: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
transitivePeerDependencies:
- supports-color
dev: true
@@ -6577,12 +6830,16 @@ packages:
convert-source-map: 2.0.0
dev: true
/varint@6.0.0:
resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==}
dev: true
/vite-hot-client@0.2.4(vite@5.4.11):
resolution: {integrity: sha512-a1nzURqO7DDmnXqabFOliz908FRmIppkBKsJthS8rbe8hBEXwEwe4C3Pp33Z1JoFCYfVL4kTOMLKk0ZZxREIeA==}
peerDependencies:
vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0
dependencies:
vite: 5.4.11(@types/node@20.17.9)
vite: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
dev: true
/vite-node@0.34.6(@types/node@20.17.9):
@@ -6595,7 +6852,7 @@ packages:
mlly: 1.7.3
pathe: 1.1.2
picocolors: 1.1.1
vite: 5.4.11(@types/node@20.17.9)
vite: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
transitivePeerDependencies:
- '@types/node'
- less
@@ -6608,7 +6865,7 @@ packages:
- terser
dev: true
/vite-node@2.1.6(@types/node@20.17.9):
/vite-node@2.1.6(@types/node@20.17.9)(sass-embedded@1.82.0):
resolution: {integrity: sha512-DBfJY0n9JUwnyLxPSSUmEePT21j8JZp/sR9n+/gBwQU6DcQOioPdb8/pibWfXForbirSagZCilseYIwaL3f95A==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
@@ -6617,7 +6874,7 @@ packages:
debug: 4.3.7
es-module-lexer: 1.5.4
pathe: 1.1.2
vite: 5.4.11(@types/node@20.17.9)
vite: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
transitivePeerDependencies:
- '@types/node'
- less
@@ -6649,7 +6906,7 @@ packages:
perfect-debounce: 1.0.0
picocolors: 1.1.1
sirv: 3.0.0
vite: 5.4.11(@types/node@20.17.9)
vite: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
transitivePeerDependencies:
- rollup
- supports-color
@@ -6675,7 +6932,7 @@ packages:
'@vue/devtools-shared': 7.6.7
execa: 9.5.1
sirv: 3.0.0
vite: 5.4.11(@types/node@20.17.9)
vite: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
vite-plugin-inspect: 0.8.8(vite@5.4.11)
vite-plugin-vue-inspector: 5.3.1(vite@5.4.11)
transitivePeerDependencies:
@@ -6699,12 +6956,12 @@ packages:
'@vue/compiler-dom': 3.5.13
kolorist: 1.8.0
magic-string: 0.30.14
vite: 5.4.11(@types/node@20.17.9)
vite: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
transitivePeerDependencies:
- supports-color
dev: true
/vite@5.4.11(@types/node@20.17.9):
/vite@5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0):
resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==}
engines: {node: ^18.0.0 || >=20.0.0}
hasBin: true
@@ -6739,6 +6996,7 @@ packages:
esbuild: 0.21.5
postcss: 8.4.49
rollup: 4.27.4
sass-embedded: 1.82.0
optionalDependencies:
fsevents: 2.3.3
dev: true
@@ -6795,7 +7053,7 @@ packages:
strip-literal: 1.3.0
tinybench: 2.9.0
tinypool: 0.7.0
vite: 5.4.11(@types/node@20.17.9)
vite: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
vite-node: 0.34.6(@types/node@20.17.9)
why-is-node-running: 2.3.0
transitivePeerDependencies:
@@ -6809,7 +7067,7 @@ packages:
- terser
dev: true
/vitest@2.1.6(@types/node@20.17.9)(jsdom@25.0.1):
/vitest@2.1.6(@types/node@20.17.9)(jsdom@25.0.1)(sass-embedded@1.82.0):
resolution: {integrity: sha512-isUCkvPL30J4c5O5hgONeFRsDmlw6kzFEdLQHLezmDdKQHy8Ke/B/dgdTMEgU0vm+iZ0TjW8GuK83DiahBoKWQ==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
@@ -6853,8 +7111,8 @@ packages:
tinyexec: 0.3.1
tinypool: 1.0.2
tinyrainbow: 1.2.0
vite: 5.4.11(@types/node@20.17.9)
vite-node: 2.1.6(@types/node@20.17.9)
vite: 5.4.11(@types/node@20.17.9)(sass-embedded@1.82.0)
vite-node: 2.1.6(@types/node@20.17.9)(sass-embedded@1.82.0)
why-is-node-running: 2.3.0
transitivePeerDependencies:
- less