mirror of
https://github.com/unraid/api.git
synced 2026-01-06 08:39:54 -06:00
fix: inject Tailwind CSS into client entry point (#1537)
Added a Vite plugin to automatically inject the Tailwind CSS import into the `unraid-components.client.js` entry file, enhancing the integration of Tailwind CSS within the application. This change improves the setup for styling components consistently across the project. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added automated validation to ensure Tailwind CSS styles are correctly included in the custom elements build output. * **Chores** * Updated the build process to include a CSS validation step after manifest generation. * Enhanced development build configuration to enable CSS source maps and optimize Tailwind CSS injection into web components. * Extended CSS theme with new responsive breakpoint variables. * Improved CSS class specificity in user profile, server state, and update modal components for consistent styling. * Removed redundant style blocks and global CSS imports from multiple components to streamline styling and reduce duplication. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -12,6 +12,12 @@
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme {
|
||||
/* Breakpoints */
|
||||
--breakpoint-xs: 30rem;
|
||||
--breakpoint-2xl: 100rem;
|
||||
--breakpoint-3xl: 120rem;
|
||||
|
||||
/* Colors */
|
||||
--color-primary-50: #fff7ed;
|
||||
--color-primary-100: #ffedd5;
|
||||
--color-primary-200: #fed7aa;
|
||||
|
||||
@@ -101,57 +101,3 @@ watchEffect(() => {
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
.unraid_mark_2,
|
||||
.unraid_mark_4 {
|
||||
animation: mark_2 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_3 {
|
||||
animation: mark_3 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_6,
|
||||
.unraid_mark_8 {
|
||||
animation: mark_6 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_7 {
|
||||
animation: mark_7 1.5s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes mark_2 {
|
||||
50% {
|
||||
transform: translateY(-40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_3 {
|
||||
50% {
|
||||
transform: translateY(-62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_6 {
|
||||
50% {
|
||||
transform: translateY(40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_7 {
|
||||
50% {
|
||||
transform: translateY(62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,8 +6,3 @@ import ApiKeyManager from '~/components/ApiKey/ApiKeyManager.vue';
|
||||
<ApiKeyManager />
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -30,9 +30,3 @@ const { authAction, stateData } = storeToRefs(serverStore);
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -14,9 +14,3 @@ onBeforeMount(() => {
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -74,9 +74,3 @@ const items = [
|
||||
<Switch id="banner" v-model:checked="form.banner" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -148,8 +148,3 @@ const onChange = ({ data }: { data: Record<string, unknown> }) => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '../../assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -23,9 +23,3 @@ import { CogIcon } from '@heroicons/vue/24/solid';
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -77,9 +77,3 @@ onBeforeMount(() => {
|
||||
</PageContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -71,9 +71,3 @@ const downloadUrl = computed(() => {
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -24,9 +24,3 @@ const items = [
|
||||
<Select v-model="selector" :items="items" placeholder="Select an initial state" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -127,10 +127,3 @@ const updateOsStatus = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -191,9 +191,3 @@ watch(selectedLogFile, (newValue) => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -31,57 +31,3 @@ const { modalVisible: apiKeyModalVisible } = storeToRefs(useApiKeyStore());
|
||||
<ApiKeyCreate :open="apiKeyModalVisible" :t="t" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
.unraid_mark_2,
|
||||
.unraid_mark_4 {
|
||||
animation: mark_2 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_3 {
|
||||
animation: mark_3 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_6,
|
||||
.unraid_mark_8 {
|
||||
animation: mark_6 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_7 {
|
||||
animation: mark_7 1.5s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes mark_2 {
|
||||
50% {
|
||||
transform: translateY(-40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_3 {
|
||||
50% {
|
||||
transform: translateY(-62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_6 {
|
||||
50% {
|
||||
transform: translateY(40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_7 {
|
||||
50% {
|
||||
transform: translateY(62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -238,8 +238,3 @@ provide('isSubmitting', isCreating);
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
</style>
|
||||
|
||||
@@ -324,9 +324,3 @@ const items = computed((): RegistrationItemProps[] => {
|
||||
</PageContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -150,9 +150,3 @@ const navigateToExternalSSOUrl = () => {
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -91,9 +91,3 @@ const handleThemeChange = (event: Event) => {
|
||||
</select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -73,57 +73,3 @@ onBeforeMount(() => {
|
||||
<UpdateOsThirdPartyDrivers v-if="rebootType === 'thirdPartyDriversDownloading'" :t="t" />
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
.unraid_mark_2,
|
||||
.unraid_mark_4 {
|
||||
animation: mark_2 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_3 {
|
||||
animation: mark_3 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_6,
|
||||
.unraid_mark_8 {
|
||||
animation: mark_6 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_7 {
|
||||
animation: mark_7 1.5s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes mark_2 {
|
||||
50% {
|
||||
transform: translateY(-40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_3 {
|
||||
50% {
|
||||
transform: translateY(-62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_6 {
|
||||
50% {
|
||||
transform: translateY(40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_7 {
|
||||
50% {
|
||||
transform: translateY(62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
KeyIcon,
|
||||
ServerStackIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import { BrandButton, BrandLoading } from '@unraid/ui';
|
||||
import { BrandButton, BrandLoading, cn } from '@unraid/ui';
|
||||
import { allowedDocsOriginRegex, allowedDocsUrlRegex } from '~/helpers/urls';
|
||||
|
||||
import type { ComposerTranslation } from 'vue-i18n';
|
||||
@@ -176,8 +176,8 @@ watch(darkMode, () => {
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="flex flex-col-reverse xs:flex-row justify-between gap-3 md:gap-4">
|
||||
<div class="flex flex-col-reverse xs:flex-row xs:justify-start gap-3 md:gap-4">
|
||||
<div :class="cn('flex flex-col-reverse xs:!flex-row justify-between gap-3 md:gap-4')">
|
||||
<div :class="cn('flex flex-col-reverse xs:!flex-row xs:justify-start gap-3 md:gap-4')">
|
||||
<!-- Back to changelog button (when navigated away) -->
|
||||
<BrandButton
|
||||
v-if="hasNavigated && docsChangelogUrl"
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
KeyIcon,
|
||||
XMarkIcon,
|
||||
} from '@heroicons/vue/24/solid';
|
||||
import { BrandButton, BrandLoading } from '@unraid/ui';
|
||||
import { BrandButton, BrandLoading, cn } from '@unraid/ui';
|
||||
import { Switch, SwitchGroup, SwitchLabel } from '@headlessui/vue';
|
||||
|
||||
import type { BrandButtonProps } from '@unraid/ui';
|
||||
@@ -276,7 +276,7 @@ const modalWidth = computed(() => {
|
||||
<template v-if="renderMainSlot" #main>
|
||||
<BrandLoading v-if="checkForUpdatesLoading" class="w-[150px] mx-auto" />
|
||||
<div v-else class="flex flex-col gap-y-4">
|
||||
<div v-if="extraLinks.length > 0" class="flex flex-col xs:flex-row justify-center gap-2">
|
||||
<div v-if="extraLinks.length > 0" :class="cn('flex flex-col xs:!flex-row justify-center gap-2')">
|
||||
<BrandButton
|
||||
v-for="item in extraLinks"
|
||||
:key="item.text"
|
||||
@@ -335,13 +335,12 @@ const modalWidth = computed(() => {
|
||||
|
||||
<template #footer>
|
||||
<div
|
||||
class="w-full flex gap-2 mx-auto"
|
||||
:class="{
|
||||
'flex-col-reverse xs:flex-row justify-between': actionButtons,
|
||||
'justify-center': !actionButtons,
|
||||
}"
|
||||
:class="cn(
|
||||
'w-full flex gap-2 mx-auto',
|
||||
actionButtons ? 'flex-col-reverse xs:!flex-row justify-between' : 'justify-center'
|
||||
)"
|
||||
>
|
||||
<div class="flex flex-col-reverse xs:flex-row justify-start gap-2">
|
||||
<div :class="cn('flex flex-col-reverse xs:!flex-row justify-start gap-2')">
|
||||
<BrandButton
|
||||
variant="underline-hover-red"
|
||||
:icon="XMarkIcon"
|
||||
@@ -355,7 +354,7 @@ const modalWidth = computed(() => {
|
||||
@click="accountStore.updateOs()"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="actionButtons" class="flex flex-col xs:flex-row justify-end gap-2">
|
||||
<div v-if="actionButtons" :class="cn('flex flex-col xs:!flex-row justify-end gap-2')">
|
||||
<BrandButton
|
||||
v-for="item in actionButtons"
|
||||
:key="item.text"
|
||||
|
||||
@@ -130,9 +130,3 @@ const downgradeButton = ref<UserProfileLink>({
|
||||
</div>
|
||||
</CardWrapper>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -262,9 +262,3 @@ watchEffect(() => {
|
||||
</div>
|
||||
</CardWrapper>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -121,9 +121,3 @@ watchEffect(() => {
|
||||
</div>
|
||||
</CardWrapper>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
|
||||
import { DropdownMenu } from '@unraid/ui';
|
||||
import { DropdownMenu, cn } from '@unraid/ui';
|
||||
import { devConfig } from '~/helpers/env';
|
||||
|
||||
import type { Server } from '~/types/server';
|
||||
@@ -109,10 +109,10 @@ onMounted(() => {
|
||||
/>
|
||||
|
||||
<div
|
||||
class="text-xs text-header-text-secondary text-right font-semibold leading-normal relative z-10 flex flex-wrap items-baseline justify-end gap-x-1 xs:flex-row xs:gap-x-4"
|
||||
:class="cn('text-xs text-header-text-secondary text-right font-semibold leading-normal relative z-10 flex flex-wrap xs:!flex-row items-baseline justify-end gap-x-1 xs:gap-x-4')"
|
||||
>
|
||||
<UpcUptimeExpire :as="'span'" :t="t" class="text-xs" />
|
||||
<span class="hidden xs:block">•</span>
|
||||
<span class="hidden xs:!block">•</span>
|
||||
<UpcServerState :t="t" class="text-xs" />
|
||||
</div>
|
||||
|
||||
@@ -121,8 +121,8 @@ onMounted(() => {
|
||||
class="text-md sm:text-lg relative flex flex-col-reverse items-end md:flex-row border-0 text-header-text-primary"
|
||||
>
|
||||
<template v-if="description && theme?.descriptionShow">
|
||||
<span class="text-right text-xs sm:text-lg hidden 2xs:block" v-html="description" />
|
||||
<span class="text-header-text-secondary hidden md:inline-block px-2">•</span>
|
||||
<span class="text-right text-xs sm:text-lg hidden 2xs:!block" v-html="description" />
|
||||
<span class="text-header-text-secondary hidden md:!inline-block px-2">•</span>
|
||||
</template>
|
||||
<button
|
||||
v-if="lanIp"
|
||||
@@ -164,57 +164,3 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
.unraid_mark_2,
|
||||
.unraid_mark_4 {
|
||||
animation: mark_2 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_3 {
|
||||
animation: mark_3 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_6,
|
||||
.unraid_mark_8 {
|
||||
animation: mark_6 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_7 {
|
||||
animation: mark_7 1.5s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes mark_2 {
|
||||
50% {
|
||||
transform: translateY(-40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_3 {
|
||||
50% {
|
||||
transform: translateY(-62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_6 {
|
||||
50% {
|
||||
transform: translateY(40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_7 {
|
||||
50% {
|
||||
transform: translateY(62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -410,57 +410,3 @@ const showUpdateEligibility = computed(() => {
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
.unraid_mark_2,
|
||||
.unraid_mark_4 {
|
||||
animation: mark_2 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_3 {
|
||||
animation: mark_3 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_6,
|
||||
.unraid_mark_8 {
|
||||
animation: mark_6 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_7 {
|
||||
animation: mark_7 1.5s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes mark_2 {
|
||||
50% {
|
||||
transform: translateY(-40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_3 {
|
||||
50% {
|
||||
transform: translateY(-62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_6 {
|
||||
50% {
|
||||
transform: translateY(40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_7 {
|
||||
50% {
|
||||
transform: translateY(62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -57,74 +57,3 @@ const showExpireTime = computed(
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
.DropdownWrapper_blip {
|
||||
box-shadow: var(--ring-offset-shadow), var(--ring-shadow), var(--shadow-foreground);
|
||||
|
||||
&::before {
|
||||
@apply absolute z-20 block;
|
||||
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
top: -10px;
|
||||
right: 42px;
|
||||
border-right: 11px solid transparent;
|
||||
border-bottom: 11px solid var(--color-popover);
|
||||
border-left: 11px solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.unraid_mark_2,
|
||||
.unraid_mark_4 {
|
||||
animation: mark_2 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_3 {
|
||||
animation: mark_3 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_6,
|
||||
.unraid_mark_8 {
|
||||
animation: mark_6 1.5s ease infinite;
|
||||
}
|
||||
.unraid_mark_7 {
|
||||
animation: mark_7 1.5s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes mark_2 {
|
||||
50% {
|
||||
transform: translateY(-40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_3 {
|
||||
50% {
|
||||
transform: translateY(-62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_6 {
|
||||
50% {
|
||||
transform: translateY(40px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes mark_7 {
|
||||
50% {
|
||||
transform: translateY(62px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -36,7 +36,7 @@ const upgradeAction = computed((): ServerStateDataAction | undefined => {
|
||||
|
||||
<template v-if="purchaseAction">
|
||||
<UpcServerStateBuy
|
||||
class="text-orange-dark relative top-px hidden sm:block"
|
||||
class="text-orange-dark relative top-px hidden sm:!block"
|
||||
:title="t('Purchase Key')"
|
||||
@click="purchaseAction.click?.()"
|
||||
>{{ t('Purchase') }}</UpcServerStateBuy>
|
||||
|
||||
@@ -79,9 +79,3 @@ watchEffect(async () => {
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style >
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
</style>
|
||||
|
||||
@@ -2,6 +2,9 @@ import { createI18n } from 'vue-i18n';
|
||||
import type { App } from 'vue';
|
||||
import { DefaultApolloClient } from '@vue/apollo-composable';
|
||||
|
||||
// Import Tailwind CSS for web components shadow DOM injection
|
||||
import tailwindStyles from '~/assets/main.css?inline';
|
||||
|
||||
import en_US from '~/locales/en_US.json';
|
||||
import { createHtmlEntityDecoder } from '~/helpers/i18n-utils';
|
||||
import { globalPinia } from '~/store/globalPinia';
|
||||
@@ -46,4 +49,19 @@ export default function (Vue: App) {
|
||||
|
||||
// Provide Apollo client for all web components
|
||||
Vue.provide(DefaultApolloClient, client);
|
||||
}
|
||||
|
||||
// Inject Tailwind CSS into the shadow DOM
|
||||
Vue.mixin({
|
||||
mounted() {
|
||||
if (typeof window !== 'undefined' && this.$el) {
|
||||
const shadowRoot = this.$el.getRootNode();
|
||||
if (shadowRoot && shadowRoot !== document && !shadowRoot.querySelector('style[data-tailwind]')) {
|
||||
const styleElement = document.createElement('style');
|
||||
styleElement.setAttribute('data-tailwind', 'true');
|
||||
styleElement.textContent = tailwindStyles;
|
||||
shadowRoot.prepend(styleElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -34,6 +34,16 @@ console.log(dropConsole ? 'WARN: Console logs are disabled' : 'INFO: Console log
|
||||
|
||||
const assetsDir = path.join(__dirname, '../api/dev/webGui/');
|
||||
|
||||
/**
|
||||
* Create a tag configuration
|
||||
*/
|
||||
const createWebComponentTag = (name: string, path: string, appContext: string) => ({
|
||||
async: false,
|
||||
name,
|
||||
path,
|
||||
appContext
|
||||
});
|
||||
|
||||
/**
|
||||
* Shared terser options for consistent minification
|
||||
*/
|
||||
@@ -190,99 +200,30 @@ export default defineNuxtConfig({
|
||||
{
|
||||
name: 'UnraidComponents',
|
||||
viteExtend(config: UserConfig) {
|
||||
return applySharedViteConfig(config, true);
|
||||
const sharedConfig = applySharedViteConfig(config, true);
|
||||
|
||||
// Optimize CSS while keeping it inlined for functionality
|
||||
if (!sharedConfig.css) sharedConfig.css = {};
|
||||
sharedConfig.css.devSourcemap = process.env.NODE_ENV === 'development';
|
||||
|
||||
return sharedConfig;
|
||||
},
|
||||
tags: [
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidAuth',
|
||||
path: '@/components/Auth.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidConnectSettings',
|
||||
path: '@/components/ConnectSettings/ConnectSettings.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidDownloadApiLogs',
|
||||
path: '@/components/DownloadApiLogs.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidHeaderOsVersion',
|
||||
path: '@/components/HeaderOsVersion.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidModals',
|
||||
path: '@/components/Modals.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidUserProfile',
|
||||
path: '@/components/UserProfile.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidUpdateOs',
|
||||
path: '@/components/UpdateOs.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidDowngradeOs',
|
||||
path: '@/components/DowngradeOs.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidRegistration',
|
||||
path: '@/components/Registration.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidWanIpCheck',
|
||||
path: '@/components/WanIpCheck.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidWelcomeModal',
|
||||
path: '@/components/Activation/WelcomeModal.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidSsoButton',
|
||||
path: '@/components/SsoButton.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidLogViewer',
|
||||
path: '@/components/Logs/LogViewer.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidThemeSwitcher',
|
||||
path: '@/components/ThemeSwitcher.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
name: 'UnraidApiKeyManager',
|
||||
path: '@/components/ApiKeyPage.ce',
|
||||
appContext: '@/components/Wrapper/web-component-plugins',
|
||||
},
|
||||
createWebComponentTag('UnraidAuth', '@/components/Auth.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidConnectSettings', '@/components/ConnectSettings/ConnectSettings.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidDownloadApiLogs', '@/components/DownloadApiLogs.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidHeaderOsVersion', '@/components/HeaderOsVersion.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidModals', '@/components/Modals.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidUserProfile', '@/components/UserProfile.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidUpdateOs', '@/components/UpdateOs.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidDowngradeOs', '@/components/DowngradeOs.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidRegistration', '@/components/Registration.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidWanIpCheck', '@/components/WanIpCheck.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidWelcomeModal', '@/components/Activation/WelcomeModal.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidSsoButton', '@/components/SsoButton.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidLogViewer', '@/components/Logs/LogViewer.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidThemeSwitcher', '@/components/ThemeSwitcher.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
createWebComponentTag('UnraidApiKeyManager', '@/components/ApiKeyPage.ce', '@/components/Wrapper/web-component-plugins'),
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
"prebuild:dev": "pnpm predev",
|
||||
"build:dev": "nuxi build --dotenv .env.staging && pnpm run manifest-ts && pnpm run deploy-to-unraid:dev",
|
||||
"build:webgui": "pnpm run type-check && nuxi build --dotenv .env.production && pnpm run manifest-ts && pnpm run copy-to-webgui-repo",
|
||||
"build": "NODE_ENV=production nuxi build --dotenv .env.production && pnpm run manifest-ts",
|
||||
"build": "NODE_ENV=production nuxi build --dotenv .env.production && pnpm run manifest-ts && pnpm run validate:css",
|
||||
"prebuild:watch": "pnpm predev",
|
||||
"build:watch": "nuxi build --dotenv .env.production --watch && pnpm run manifest-ts",
|
||||
"generate": "nuxt generate",
|
||||
"manifest-ts": "node ./scripts/add-timestamp-webcomponent-manifest.js",
|
||||
"validate:css": "node ./scripts/validate-custom-elements-css.js",
|
||||
"// Deployment": "",
|
||||
"unraid:deploy": "pnpm build:dev",
|
||||
"deploy-to-unraid:dev": "./scripts/deploy-dev.sh",
|
||||
|
||||
@@ -220,17 +220,3 @@ watch(
|
||||
<Toaster rich-colors close-button />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* Import unraid-ui globals first */
|
||||
@import '@unraid/ui/styles';
|
||||
@import '~/assets/main.css';
|
||||
|
||||
code {
|
||||
@apply rounded-lg bg-gray-200 p-1 text-black shadow;
|
||||
}
|
||||
|
||||
pre {
|
||||
@apply overflow-x-scroll py-3;
|
||||
}
|
||||
</style>
|
||||
|
||||
158
web/scripts/validate-custom-elements-css.js
Normal file
158
web/scripts/validate-custom-elements-css.js
Normal file
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Recursively find JS files in a directory
|
||||
*/
|
||||
function findJSFiles(dir, jsFiles = []) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
return jsFiles;
|
||||
}
|
||||
|
||||
const items = fs.readdirSync(dir);
|
||||
for (const item of items) {
|
||||
const fullPath = path.join(dir, item);
|
||||
const stat = fs.statSync(fullPath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
findJSFiles(fullPath, jsFiles);
|
||||
} else if (item.endsWith('.js')) {
|
||||
jsFiles.push(fullPath);
|
||||
}
|
||||
}
|
||||
return jsFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that Tailwind CSS styles are properly inlined in the JavaScript bundle
|
||||
*/
|
||||
function validateCustomElementsCSS() {
|
||||
console.log('🔍 Validating custom elements JS bundle includes inlined Tailwind styles...');
|
||||
|
||||
try {
|
||||
// Find the custom elements JS files
|
||||
const customElementsDir = '.nuxt/nuxt-custom-elements/dist';
|
||||
const jsFiles = findJSFiles(customElementsDir);
|
||||
|
||||
if (jsFiles.length === 0) {
|
||||
throw new Error('No custom elements JS files found in ' + customElementsDir);
|
||||
}
|
||||
|
||||
// Find the largest JS file (likely the main bundle with inlined CSS)
|
||||
const jsFile = jsFiles.reduce((largest, current) => {
|
||||
const currentSize = fs.statSync(current).size;
|
||||
const largestSize = fs.statSync(largest).size;
|
||||
return currentSize > largestSize ? current : largest;
|
||||
});
|
||||
console.log(`📁 Checking JS bundle: ${jsFile}`);
|
||||
|
||||
// Read the JS content
|
||||
const jsContent = fs.readFileSync(jsFile, 'utf8');
|
||||
|
||||
// Define required Tailwind indicators (looking for inlined CSS in JS)
|
||||
const requiredIndicators = [
|
||||
{
|
||||
name: 'Tailwind utility classes (inline)',
|
||||
pattern: /\.flex\s*\{[^}]*display:\s*flex/,
|
||||
description: 'Basic Tailwind utility classes inlined'
|
||||
},
|
||||
{
|
||||
name: 'Tailwind margin utilities (inline)',
|
||||
pattern: /\.m-\d+\s*\{[^}]*margin:/,
|
||||
description: 'Tailwind margin utilities inlined'
|
||||
},
|
||||
{
|
||||
name: 'Tailwind padding utilities (inline)',
|
||||
pattern: /\.p-\d+\s*\{[^}]*padding:/,
|
||||
description: 'Tailwind padding utilities inlined'
|
||||
},
|
||||
{
|
||||
name: 'Tailwind color utilities (inline)',
|
||||
pattern: /\.text-\w+\s*\{[^}]*color:/,
|
||||
description: 'Tailwind text color utilities inlined'
|
||||
},
|
||||
{
|
||||
name: 'Tailwind background utilities (inline)',
|
||||
pattern: /\.bg-\w+\s*\{[^}]*background/,
|
||||
description: 'Tailwind background utilities inlined'
|
||||
},
|
||||
{
|
||||
name: 'CSS custom properties',
|
||||
pattern: /--[\w-]+:\s*[^;]+;/,
|
||||
description: 'CSS custom properties (variables)'
|
||||
},
|
||||
{
|
||||
name: 'Responsive breakpoints',
|
||||
pattern: /@media\s*\([^)]*min-width/,
|
||||
description: 'Responsive media queries'
|
||||
},
|
||||
{
|
||||
name: 'CSS reset styles',
|
||||
pattern: /\*[^}]*box-sizing|box-sizing[^}]*border-box/,
|
||||
description: 'Tailwind CSS reset/normalize styles'
|
||||
}
|
||||
];
|
||||
|
||||
// Validate each indicator
|
||||
const results = [];
|
||||
let allPassed = true;
|
||||
|
||||
for (const indicator of requiredIndicators) {
|
||||
const found = indicator.pattern.test(jsContent);
|
||||
results.push({
|
||||
name: indicator.name,
|
||||
description: indicator.description,
|
||||
passed: found
|
||||
});
|
||||
|
||||
if (!found) {
|
||||
allPassed = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Report results
|
||||
console.log('\n📊 Validation Results:');
|
||||
console.log('====================');
|
||||
|
||||
for (const result of results) {
|
||||
const status = result.passed ? '✅' : '❌';
|
||||
console.log(`${status} ${result.name}`);
|
||||
if (!result.passed) {
|
||||
console.log(` └─ Missing: ${result.description}`);
|
||||
}
|
||||
}
|
||||
|
||||
// File size check
|
||||
const fileSizeKB = Math.round(fs.statSync(jsFile).size / 1024);
|
||||
console.log(`\n📏 JS bundle size: ${fileSizeKB} KB`);
|
||||
|
||||
if (fileSizeKB < 1000) {
|
||||
console.log('⚠️ WARNING: JS bundle seems too small, inlined Tailwind styles might not be included');
|
||||
allPassed = false;
|
||||
} else {
|
||||
console.log('✅ JS bundle size looks good');
|
||||
}
|
||||
|
||||
// Final result
|
||||
if (allPassed) {
|
||||
console.log('\n🎉 SUCCESS: All Tailwind styles are properly inlined in the JS bundle!');
|
||||
process.exit(0);
|
||||
} else {
|
||||
console.log('\n❌ FAILURE: Some Tailwind styles are missing from the JS bundle!');
|
||||
console.log('\n💡 This might indicate:');
|
||||
console.log(' - The CSS inline import in viteExtend is not working properly');
|
||||
console.log(' - Tailwind configuration is not being processed');
|
||||
console.log(' - CSS is not being injected into shadow DOM components');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ ERROR during validation:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the validation
|
||||
validateCustomElementsCSS();
|
||||
Reference in New Issue
Block a user