chore(deps): bump tailwindcss to v4

This commit is contained in:
2026-02-10 10:29:50 +08:00
parent 2c4951ff9b
commit d0bca215c1
35 changed files with 117 additions and 88 deletions

View File

@@ -1,6 +1,24 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import 'tailwindcss';
@config '../../../tailwind.config.ts';
/*
The default border color has changed to `currentcolor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentcolor);
}
}
@layer base {
html {

View File

@@ -30,7 +30,7 @@ const props = defineProps({
</h1>
<h1
class="text-xl font-bold text-neutral-700 dark:text-neutral-300 leading-none relative z-[1]"
class="text-xl font-bold text-neutral-700 dark:text-neutral-300 leading-none relative z-1"
>
{{ title }}
</h1>
@@ -43,7 +43,7 @@ const props = defineProps({
<div
v-if="bubble"
:class="`bg-${bubbleColor}/50`"
class="absolute -left-1.5 -bottom-1.5 w-4 h-4 rounded-full z-[0]"
class="absolute -left-1.5 -bottom-1.5 w-4 h-4 rounded-full z-0"
></div>
</div>
</template>

View File

@@ -477,7 +477,7 @@ const showAuthModal = ref(false)
<div class="flex items-center gap-2">
<UIcon
name="i-heroicons-sun"
class="h-4 w-4 text-amber-500 mt-0.5 flex-shrink-0"
class="h-4 w-4 text-amber-500 mt-0.5 shrink-0"
/>
<span class="text-xs text-gray-600 dark:text-gray-300">
确保光线充足避免背光
@@ -486,7 +486,7 @@ const showAuthModal = ref(false)
<div class="flex items-center gap-2">
<UIcon
name="i-heroicons-speaker-wave"
class="h-4 w-4 text-green-500 mt-0.5 flex-shrink-0"
class="h-4 w-4 text-green-500 mt-0.5 shrink-0"
/>
<span class="text-xs text-gray-600 dark:text-gray-300">
选择安静环境减少噪音干扰
@@ -495,7 +495,7 @@ const showAuthModal = ref(false)
<div class="flex items-center gap-2">
<UIcon
name="i-heroicons-viewfinder-circle"
class="h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0"
class="h-4 w-4 text-blue-500 mt-0.5 shrink-0"
/>
<span class="text-xs text-gray-600 dark:text-gray-300">
人脸占画面比例控制在 1/4 以内
@@ -504,7 +504,7 @@ const showAuthModal = ref(false)
<div class="flex items-center gap-2">
<UIcon
name="i-heroicons-face-smile"
class="h-4 w-4 text-purple-500 mt-0.5 flex-shrink-0"
class="h-4 w-4 text-purple-500 mt-0.5 shrink-0"
/>
<span class="text-xs text-gray-600 dark:text-gray-300">
保持自然表情使用恰当手势

View File

@@ -18,12 +18,12 @@ const props = defineProps({
<template>
<div
:class="{
'w-full h-[1px]': !vertical,
'w-[1px] h-full': vertical,
'w-full h-px': !vertical,
'w-px h-full': vertical,
[`from-${lineGradientFrom}-500/50`]: true,
[`to-${lineGradientTo}-300/50`]: true,
}"
class="bg-gradient-to-r rounded-full my-4"
class="bg-linear-to-r rounded-full my-4"
></div>
</template>

View File

@@ -36,6 +36,6 @@ const size = computed(() => {
<style scoped>
.gradient-background {
@apply rounded-lg;
@apply bg-gradient-to-r from-indigo-800 to-purple-600;
@apply bg-linear-to-r from-indigo-800 to-purple-600;
}
</style>

View File

@@ -585,7 +585,7 @@ const onForgetPasswordSubmit = (
.pin-input {
@apply w-full md:w-16 aspect-square rounded text-center shadow caret-transparent;
@apply outline-0 ring-indigo-500 focus:ring font-bold;
@apply outline-0 ring-indigo-500 focus:ring-3 font-bold;
}
.pin-label {

View File

@@ -68,7 +68,7 @@ const getShapeSize = (r: { w: number; h: number }, size: number) => {
:class="[ratio.value === selected && 'bg-sky-200/50 dark:bg-sky-700/50']"
>
<div
class="bg-neutral-300/50 dark:bg-neutral-600/50 text-neutral-600 dark:text-neutral-300 rounded flex justify-center items-center"
class="bg-neutral-300/50 dark:bg-neutral-600/50 text-neutral-600 dark:text-neutral-300 rounded-sm flex justify-center items-center"
:class="[
ratio.value === selected && 'bg-sky-300/50 dark:bg-sky-600/50',
]"

View File

@@ -58,7 +58,7 @@ const handleFileInput = (event: { target: any }) => {
<template>
<div
class="w-full bg-neutral-200/50 dark:bg-neutral-700/50 rounded-md flex justify-between items-center p-1.5 gap-2 relative hover:bg-neutral-200/80 hover:dark:bg-neutral-700/80 transition border dark:border-neutral-700 cursor-pointer"
class="w-full bg-neutral-200/50 dark:bg-neutral-700/50 rounded-md flex justify-between items-center p-1.5 gap-2 relative hover:bg-neutral-200/80 dark:hover:bg-neutral-700/80 transition border dark:border-neutral-700 cursor-pointer"
:class="{ 'cursor-pointer': !loading, 'cursor-not-allowed': loading }"
@click="() => !loading && fileInput?.click()"
>

View File

@@ -25,7 +25,7 @@ const message_avatar = computed(() => {
})
const message_background = computed(() => {
if (props.message?.interrupted) {
return 'bg-red-200/50 dark:bg-red-800/20 border-red-300 dark:!border-red-500/50'
return 'bg-red-200/50 dark:bg-red-800/20 border-red-300 dark:border-red-500/50!'
}
switch (props.message?.role) {
case 'user':

View File

@@ -40,7 +40,7 @@ const { data: assistantTemplates, pending: assistantTemplatesPending } =
<Transition name="loading-screen">
<div
v-if="assistantTemplatesPending"
class="absolute inset-0 bg-white dark:bg-neutral-900 flex justify-center items-center z-[1] text-primary"
class="absolute inset-0 bg-white dark:bg-neutral-900 flex justify-center items-center z-1 text-primary"
>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -151,7 +151,7 @@ const { data: assistantTemplates, pending: assistantTemplatesPending } =
class="group ring-primary hover:ring-2 transition duration-300"
variant="soft"
size="lg"
:ui="{ rounded: 'rounded-full' }"
:ui="{ rounded-sm: 'rounded-full' }"
@click="emit('select', null)"
>
<span class="-mt-0.5">直接开始</span>
@@ -202,7 +202,7 @@ const { data: assistantTemplates, pending: assistantTemplatesPending } =
}
.assistant-item {
@apply w-full bg-white dark:bg-neutral-800 rounded-lg shadow-sm ring-primary ring-offset-2 dark:ring-offset-0 hover:ring-2 transition;
@apply w-full bg-white dark:bg-neutral-800 rounded-lg shadow-xs ring-primary ring-offset-2 dark:ring-offset-0 hover:ring-2 transition;
@apply flex items-center gap-4 px-4 py-2 cursor-pointer border dark:border-neutral-700 hover:border-transparent;
}
</style>

View File

@@ -17,7 +17,7 @@ const props = defineProps({
<template>
<div
class="bg-neutral-50 dark:bg-neutral-900 px-1.5 py-1 rounded flex flex-col gap-1 shadow"
class="bg-neutral-50 dark:bg-neutral-900 px-1.5 py-1 rounded-sm flex flex-col gap-1 shadow-sm"
>
<div class="flex items-center gap-1 text-sm">
<UIcon

View File

@@ -155,7 +155,7 @@ const copyToClipboard = (text: string) => {
:key="`${fid}-${i}`"
>
<div
class="absolute inset-0 bg-gradient-to-t from-neutral-800/40 to-transparent w-full h-full flex items-end scale-105 opacity-0 group-hover:scale-100 group-hover:opacity-100 transition"
class="absolute inset-0 bg-linear-to-t from-neutral-800/40 to-transparent w-full h-full flex items-end scale-105 opacity-0 group-hover:scale-100 group-hover:opacity-100 transition"
>
<div class="w-full flex justify-end gap-1 p-1">
<UTooltip text="以此图为参考创作">
@@ -189,7 +189,7 @@ const copyToClipboard = (text: string) => {
</div>
<div
v-else
class="h-64 aspect-[3/4] mb-4 rounded-lg placeholder-gradient flex justify-center items-center"
class="h-64 aspect-3/4 mb-4 rounded-lg placeholder-gradient flex justify-center items-center"
>
<UIcon
name="i-svg-spinners-tadpole"
@@ -298,6 +298,6 @@ const copyToClipboard = (text: string) => {
}
.placeholder-gradient {
@apply animate-pulse bg-gradient-to-br from-neutral-200 to-neutral-300 dark:from-neutral-700 dark:to-neutral-800;
@apply animate-pulse bg-linear-to-br from-neutral-200 to-neutral-300 dark:from-neutral-700 dark:to-neutral-800;
}
</style>

View File

@@ -236,7 +236,7 @@ const onRetryClick = (course: resp.gen.CourseGenItem) => {
<template>
<div
class="w-full rounded-xl border border-neutral-200 dark:border-neutral-700 hover:shadow transition overflow-hidden"
class="w-full rounded-xl border border-neutral-200 dark:border-neutral-700 hover:shadow-sm transition overflow-hidden"
>
<div class="relative w-full aspect-video group">
<NuxtImg
@@ -248,7 +248,7 @@ const onRetryClick = (course: resp.gen.CourseGenItem) => {
/>
<div
v-else
class="absolute inset-0 bg-gradient-to-br from-purple-400 to-primary-400 flex justify-center items-center pattern"
class="absolute inset-0 bg-linear-to-br from-purple-400 to-primary-400 flex justify-center items-center pattern"
>
<Icon
v-if="isFailed"
@@ -269,7 +269,7 @@ const onRetryClick = (course: resp.gen.CourseGenItem) => {
<UBadge
:color="stateDisplay.color"
:variant="isFailed ? 'solid' : 'subtle'"
class="shadow"
class="shadow-sm"
size="sm"
>
<Icon
@@ -462,7 +462,7 @@ const onRetryClick = (course: resp.gen.CourseGenItem) => {
</template>
<video
class="w-full rounded shadow"
class="w-full rounded-sm shadow-sm"
controls
autoplay
:src="course.video_url"

View File

@@ -197,10 +197,10 @@ const startDownload = (url: string, filename: string) => {
<template>
<div
class="w-full flex gap-2 rounded-xl border border-neutral-200 dark:border-neutral-700 hover:shadow transition overflow-hidden p-3"
class="w-full flex gap-2 rounded-xl border border-neutral-200 dark:border-neutral-700 hover:shadow-sm transition overflow-hidden p-3"
>
<div
class="flex-0 h-48 aspect-[10/16] flex flex-col items-center justify-center rounded-lg shadow overflow-hidden relative group"
class="flex-0 h-48 aspect-10/16 flex flex-col items-center justify-center rounded-lg shadow-sm overflow-hidden relative group"
>
<div
v-if="!video.video_cover"
@@ -451,7 +451,7 @@ const startDownload = (url: string, filename: string) => {
</template>
<video
class="w-full rounded shadow"
class="w-full rounded-sm shadow-sm"
controls
autoplay
:src="video.video_url"
@@ -570,7 +570,7 @@ const startDownload = (url: string, filename: string) => {
视频预览
</div>
<video
class="w-full rounded-lg shadow bg-black"
class="w-full rounded-lg shadow-sm bg-black"
controls
autoplay
muted

View File

@@ -41,7 +41,7 @@ type subtitleStyleSchema = InferType<typeof subtitleStyleSchema>
const subtitleStyleState = reactive<subtitleStyleSchema>({
color: '#fff',
effect: 'shadow',
effect: 'shadow-sm',
fontSize: 24,
bottomOffset: 12,
})
@@ -213,7 +213,7 @@ const exportVideo = async () => {
color: subtitleStyleState.color,
fontSize: subtitleStyleState.fontSize,
textShadow:
subtitleStyleState.effect === 'shadow'
subtitleStyleState.effect === 'shadow-sm'
? {
offsetX: 2,
offsetY: 2,
@@ -381,7 +381,7 @@ defineExpose({
fontSize: subtitleStyleState.fontSize / 1.5 + 'px',
bottom: subtitleStyleState.bottomOffset / 1.5 + 'px',
textShadow:
subtitleStyleState.effect === 'shadow'
subtitleStyleState.effect === 'shadow-sm'
? '2px 2px 4px rgba(0, 0, 0, 0.25)'
: undefined,
}"
@@ -391,7 +391,7 @@ defineExpose({
<video
controls
ref="videoElement"
class="rounded"
class="rounded-sm"
style="-webkit-user-drag: none"
:src="course.video_url"
@timeupdate="syncSubtitles"
@@ -425,7 +425,7 @@ defineExpose({
fontSize: subtitleStyleState.fontSize / 1.5 + 'px',
bottom: subtitleStyleState.bottomOffset / 1.5 + 'px',
textShadow:
subtitleStyleState.effect === 'shadow'
subtitleStyleState.effect === 'shadow-sm'
? '2px 2px 4px rgba(0, 0, 0, 0.25)'
: undefined,
}"
@@ -475,7 +475,7 @@ defineExpose({
:options="[
{
label: '阴影',
value: 'shadow',
value: 'shadow-sm',
},
{
label: '描边',
@@ -606,7 +606,7 @@ defineExpose({
content: '';
inset: 80% 0 0;
position: absolute;
@apply bg-gradient-to-b from-transparent to-white dark:to-neutral-950 pointer-events-none;
@apply bg-linear-to-b from-transparent to-white dark:to-neutral-950 pointer-events-none;
}
.subtitle.stroke {

View File

@@ -38,12 +38,12 @@ const closePreview = () => {
<template>
<div
class="relative w-full flex flex-col rounded-lg border border-neutral-200 dark:border-neutral-700 overflow-hidden shadow-none hover:shadow transition-shadow"
class="relative w-full flex flex-col rounded-lg border border-neutral-200 dark:border-neutral-700 overflow-hidden shadow-none hover:shadow-sm transition-shadow"
>
<div class="relative w-full aspect-[16/9] group">
<div class="relative w-full aspect-16/9 group">
<NuxtImg
placeholder
placeholder-class="w-full aspect-[16/9] object-cover bg-neutral-200 dark:bg-neutral-800"
placeholder-class="w-full aspect-16/9 object-cover bg-neutral-200 dark:bg-neutral-800"
class="object-cover relative"
:src="data.opening_url"
/>
@@ -183,7 +183,7 @@ const closePreview = () => {
<video
v-if="previewVideoUrl"
class="w-full rounded shadow"
class="w-full rounded-sm shadow-sm"
controls
autoplay
:src="previewVideoUrl"

View File

@@ -55,7 +55,7 @@ const handleClick = (e: any) => {
<template>
<button
class="w-fit flex justify-center items-center rounded-md font-bold border shadow-sm transition focus:ring-4"
class="w-fit flex justify-center items-center rounded-md font-bold border shadow-xs transition focus:ring-4"
:class="{
'w-full': block,
'uni-button--disabled': disabled || loading,

View File

@@ -98,9 +98,9 @@ const handleInput = (e: any) => {
</p>
<div class="relative">
<input
class="relative w-full flex items-center gap-2.5 p-2 pr-2 rounded-md overflow-hidden border transition bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-800 focus:border-neutral-400 dark:focus:border-neutral-700 focus:ring-4 focus:ring-opacity-50 focus:ring-neutral-200 dark:focus:ring-neutral-800 outline-none placeholder-neutral-400 dark:placeholder-neutral-500 shadow-sm"
class="relative w-full flex items-center gap-2.5 p-2 pr-2 rounded-md overflow-hidden border transition bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-800 focus:border-neutral-400 dark:focus:border-neutral-700 focus:ring-4 focus:ring-opacity-50 focus:ring-neutral-200 dark:focus:ring-neutral-800 outline-hidden placeholder-neutral-400 dark:placeholder-neutral-500 shadow-xs"
:class="{
'!border-red-500': isError,
'border-red-500!': isError,
'bg-neutral-100 dark:bg-neutral-900 text-neutral-400 dark:text-neutral-600':
disabled,
}"

View File

@@ -83,7 +83,7 @@ nuxtApp.vueApp.provide('uni-message', api)
<style scoped>
#message-provider .message-wrapper {
@apply z-[50000] fixed inset-0 flex flex-col items-center pointer-events-none;
@apply z-50000 fixed inset-0 flex flex-col items-center pointer-events-none;
}
.message-move,

View File

@@ -26,12 +26,12 @@ onMounted(() => {
<div
class="message"
:class="{
'!text-blue-500 !border-blue-400 !bg-blue-50': message.type === 'info',
'!text-emerald-500 !border-emerald-400 !bg-emerald-50':
'text-blue-500! border-blue-400! bg-blue-50!': message.type === 'info',
'text-emerald-500! border-emerald-400! bg-emerald-50!':
message.type === 'success',
'!text-orange-500 !border-orange-400 !bg-orange-50':
'text-orange-500! border-orange-400! bg-orange-50!':
message.type === 'warning',
'!text-rose-500 !border-rose-400 !bg-rose-50': message.type === 'error',
'text-rose-500! border-rose-400! bg-rose-50!': message.type === 'error',
[message.type]: message.type,
}"
>

View File

@@ -97,7 +97,7 @@ onMounted(() => {
ref="selectWrapperRef"
>
<button
class="relative w-full flex items-center gap-2.5 p-2 pr-6 rounded-md overflow-hidden border transition bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-800 focus:border-neutral-400 dark:focus:border-neutral-700 focus:ring-4 focus:ring-opacity-50 focus:ring-neutral-200 dark:focus:ring-neutral-800 shadow-sm"
class="relative w-full flex items-center gap-2.5 p-2 pr-6 rounded-md overflow-hidden border transition bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-800 focus:border-neutral-400 dark:focus:border-neutral-700 focus:ring-4 focus:ring-opacity-50 focus:ring-neutral-200 dark:focus:ring-neutral-800 shadow-xs"
:class="{
'cursor-not-allowed bg-neutral-100 dark:bg-neutral-900 text-neutral-400 dark:text-neutral-600':
disabled,
@@ -148,9 +148,9 @@ onMounted(() => {
v-for="(option, index) in items"
:key="index"
:class="{
'!bg-neutral-200 dark:!bg-neutral-700 hover:!bg-neutral-200 dark:hover:!bg-neutral-700':
'bg-neutral-200! dark:bg-neutral-700! hover:bg-neutral-200! dark:hover:bg-neutral-700!':
option.value === selectedItem?.value,
'!cursor-not-allowed text-neutral-300 dark:text-neutral-500 hover:bg-white dark:hover:!bg-neutral-800':
'cursor-not-allowed! text-neutral-300 dark:text-neutral-500 hover:bg-white dark:hover:bg-neutral-800!':
option.disabled,
}"
@click="!option.disabled ? handleOptionSelect(option) : void 0"

View File

@@ -116,11 +116,11 @@ onMounted(() => {
</p>
<div class="relative">
<textarea
class="relative w-full flex items-center gap-2.5 p-2 pr-6 rounded-md overflow-hidden overflow-y-auto border transition bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-800 focus:border-neutral-400 dark:focus:border-neutral-700 focus:ring-4 focus:ring-opacity-50 focus:ring-neutral-200 dark:focus:ring-neutral-800 outline-none placeholder-neutral-400 dark:placeholder-neutral-500 shadow-sm"
class="relative w-full flex items-center gap-2.5 p-2 pr-6 rounded-md overflow-hidden overflow-y-auto border transition bg-white dark:bg-neutral-800 border-neutral-200 dark:border-neutral-800 focus:border-neutral-400 dark:focus:border-neutral-700 focus:ring-4 focus:ring-opacity-50 focus:ring-neutral-200 dark:focus:ring-neutral-800 outline-hidden placeholder-neutral-400 dark:placeholder-neutral-500 shadow-xs"
:rows="minRows || rows"
ref="textAreaRef"
:class="{
'!border-red-500': isError,
'border-red-500!': isError,
'bg-neutral-100 dark:bg-neutral-900 text-neutral-400 dark:text-neutral-600':
disabled,
}"

View File

@@ -98,18 +98,18 @@ watch(
<template>
<button
class="relative flex items-center rounded-lg bg-neutral-100 dark:bg-neutral-800 shadow-inner transition ease-in-out group outline-none"
class="relative flex items-center rounded-lg bg-neutral-100 dark:bg-neutral-800 shadow-inner transition ease-in-out group outline-hidden"
:class="{
'!bg-green-400 dark:!bg-green-400/50': checked,
'bg-green-400! dark:bg-green-400/50!': checked,
[buttonSizeClass]: buttonSizeClass,
[buttonPaddingClass]: buttonPaddingClass,
}"
@click="handleCheck"
>
<span
class="aspect-[1/1] translate-x-0 transition ease-in-out bg-white dark:bg-black rounded-md shadow duration-300 group-active:scale-90"
class="aspect-1/1 translate-x-0 transition ease-in-out bg-white dark:bg-black rounded-md shadow-sm duration-300 group-active:scale-90"
:class="{
'!shadow-lg': checked,
'shadow-lg!': checked,
'group-active:translate-x-3 group-active:duration-500': !checked,
[bulletSizeClass]: bulletSizeClass,
[bulletTranslateClass]: checked,

View File

@@ -208,7 +208,7 @@ onMounted(async () => {
<span class="truncate">{{ item.label }}</span>
<UIcon
:name="item.icon"
class="ms-auto size-4 flex-shrink-0 text-gray-400 dark:text-gray-500"
class="ms-auto size-4 shrink-0 text-gray-400 dark:text-gray-500"
/>
</template>
</UDropdown>

View File

@@ -488,7 +488,7 @@ onMounted(() => {
v-for="(llm, index) in llmModels"
:key="index"
@click="currentModel = llm.tag"
class="flex flex-col gap-2 justify-center items-center w-full aspect-[1/1] border-2 rounded-xl cursor-pointer transition duration-150 select-none"
class="flex flex-col gap-2 justify-center items-center w-full aspect-1/1 border-2 rounded-xl cursor-pointer transition duration-150 select-none"
:class="
llm.tag === currentModel
? 'border-primary shadow-xl bg-primary text-white'
@@ -501,7 +501,7 @@ onMounted(() => {
class="text-4xl opacity-80"
/>
<div class="flex flex-col gap-0.5 items-center">
<h1 class="font-bold drop-shadow opacity-90">
<h1 class="font-bold drop-shadow-sm opacity-90">
{{ llm.name || 'unknown' }}
</h1>
<p class="text-xs opacity-60">{{ llm.description }}</p>
@@ -548,7 +548,7 @@ onMounted(() => {
.chat-option-btn {
@apply text-lg px-2 py-1.5 flex gap-1 justify-center items-center rounded-lg;
@apply bg-white border border-neutral-300 shadow-sm hover:shadow-card;
@apply bg-white border border-neutral-300 shadow-xs hover:shadow-card;
@apply dark:bg-neutral-800 dark:border-neutral-600;
}
</style>

View File

@@ -333,7 +333,7 @@ const onDefaultFormSubmit = (event: FormSubmitEvent<DefaultFormSchema>) => {
@mousedown.prevent="handle_stick_mousedown"
>
<span
class="w-[1px] h-full bg-neutral-300 dark:bg-neutral-700 group-hover:bg-indigo-300 dark:group-hover:bg-indigo-700 group-hover:w-[3px] transition-all group-hover:delay-500 translate-x-1"
class="w-px h-full bg-neutral-300 dark:bg-neutral-700 group-hover:bg-indigo-300 dark:group-hover:bg-indigo-700 group-hover:w-[3px] transition-all group-hover:delay-500 translate-x-1"
></span>
</div>
<div

View File

@@ -200,7 +200,7 @@ const open = (url?: string | URL, target?: string, features?: string) => {
>
<div class="container max-w-[1280px] mx-auto py-4 space-y-12">
<div
class="pattern w-full p-10 flex flex-col justify-center gap-3 items-center rounded-lg shadow-sm border border-gray-200 dark:border-neutral-700"
class="pattern w-full p-10 flex flex-col justify-center gap-3 items-center rounded-lg shadow-xs border border-gray-200 dark:border-neutral-700"
>
<h1 class="text-4xl font-bold text-center text-primary">AI 工具导航</h1>
<p>常用 AI 工具一网打尽常用常新</p>
@@ -222,7 +222,7 @@ const open = (url?: string | URL, target?: string, features?: string) => {
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<div
v-for="link in cat.links"
class="bg-white dark:bg-neutral-800 p-4 rounded-lg shadow-sm border border-gray-200 dark:border-neutral-700 space-y-2 cursor-pointer hover:shadow-md transition-all duration-300"
class="bg-white dark:bg-neutral-800 p-4 rounded-lg shadow-xs border border-gray-200 dark:border-neutral-700 space-y-2 cursor-pointer hover:shadow-md transition-all duration-300"
:key="link.id"
@click="open(link.url)"
>

View File

@@ -323,7 +323,7 @@ const previewVideo = (videoUrl: string, title: string) => {
const closeButton = document.createElement('button')
closeButton.textContent = '关闭'
closeButton.className =
'mt-4 px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600'
'mt-4 px-4 py-2 bg-gray-500 text-white rounded-sm hover:bg-gray-600'
closeButton.onclick = () => {
document.body.removeChild(videoModal)
}

View File

@@ -947,7 +947,7 @@ const onCreateSubmit = async (
v-if="row.info.opening_url"
:src="row.info.opening_url"
:alt="row.info.title"
class="w-16 h-9 object-cover rounded cursor-pointer hover:opacity-80 transition-opacity"
class="w-16 h-9 object-cover rounded-sm cursor-pointer hover:opacity-80 transition-opacity"
@click="
previewVideo(
row.info.opening_file,
@@ -989,12 +989,12 @@ const onCreateSubmit = async (
v-if="row.opening_url"
:src="row.opening_url"
alt="片头"
class="w-14 h-8 object-cover rounded cursor-pointer hover:opacity-80 transition-opacity ring-1 ring-blue-200 dark:ring-blue-800"
class="w-14 h-8 object-cover rounded-sm cursor-pointer hover:opacity-80 transition-opacity ring-1 ring-blue-200 dark:ring-blue-800"
@click="previewVideo(row.opening_file, '制作片头预览')"
/>
<div
v-else
class="w-14 h-8 bg-blue-100 dark:bg-blue-900/30 rounded flex items-center justify-center cursor-pointer hover:opacity-80 transition-opacity"
class="w-14 h-8 bg-blue-100 dark:bg-blue-900/30 rounded-sm flex items-center justify-center cursor-pointer hover:opacity-80 transition-opacity"
@click="previewVideo(row.opening_file, '制作片头预览')"
>
<UIcon
@@ -1015,12 +1015,12 @@ const onCreateSubmit = async (
v-if="row.ending_url"
:src="row.ending_url"
alt="片尾"
class="w-14 h-8 object-cover rounded cursor-pointer hover:opacity-80 transition-opacity ring-1 ring-green-200 dark:ring-green-800"
class="w-14 h-8 object-cover rounded-sm cursor-pointer hover:opacity-80 transition-opacity ring-1 ring-green-200 dark:ring-green-800"
@click="previewVideo(row.ending_file, '制作片尾预览')"
/>
<div
v-else
class="w-14 h-8 bg-green-100 dark:bg-green-900/30 rounded flex items-center justify-center cursor-pointer hover:opacity-80 transition-opacity"
class="w-14 h-8 bg-green-100 dark:bg-green-900/30 rounded-sm flex items-center justify-center cursor-pointer hover:opacity-80 transition-opacity"
@click="previewVideo(row.ending_file, '制作片尾预览')"
>
<UIcon
@@ -1364,7 +1364,7 @@ const onCreateSubmit = async (
v-if="previewVideoUrl"
:src="previewVideoUrl"
controls
class="w-full h-full rounded"
class="w-full h-full rounded-sm"
/>
</div>
</UCard>
@@ -1395,7 +1395,7 @@ const onCreateSubmit = async (
v-if="previewImageUrl"
:src="previewImageUrl"
:alt="previewImageTitle"
class="max-w-full max-h-[70vh] rounded object-contain"
class="max-w-full max-h-[70vh] rounded-sm object-contain"
/>
</div>
</UCard>

View File

@@ -305,14 +305,14 @@ const onAvatarUpload = async (files: FileList) => {
? systemAvatarList?.data.items
: userAvatarList?.data.items"
:key="avatar.model_id || k"
class="relative rounded-lg shadow overflow-hidden w-full aspect-[9/16] group"
class="relative rounded-lg shadow-sm overflow-hidden w-full aspect-9/16 group"
>
<NuxtImg
:src="avatar.avatar"
class="w-full h-full object-cover"
/>
<div
class="absolute inset-x-0 bottom-0 p-2 bg-gradient-to-t from-black/50 to-transparent flex gap-2"
class="absolute inset-x-0 bottom-0 p-2 bg-linear-to-t from-black/50 to-transparent flex gap-2"
>
<UBadge
color="white"
@@ -334,7 +334,7 @@ const onAvatarUpload = async (files: FileList) => {
</template>
</div>
<div
class="absolute inset-0 flex flex-col justify-center items-center bg-white/50 dark:bg-neutral-800/50 backdrop-blur opacity-0 group-hover:opacity-100 transition-opacity"
class="absolute inset-0 flex flex-col justify-center items-center bg-white/50 dark:bg-neutral-800/50 backdrop-blur-sm opacity-0 group-hover:opacity-100 transition-opacity"
>
<UButtonGroup>
<UButton
@@ -376,7 +376,7 @@ const onAvatarUpload = async (files: FileList) => {
<template #avatar-data="{ row }">
<NuxtImg
:src="row.avatar"
class="h-16 aspect-[9/16] rounded-lg"
class="h-16 aspect-9/16 rounded-lg"
/>
</template>
<template #type-data="{ row }">

View File

@@ -327,7 +327,7 @@ const onDeleteCat = (cat: PPTCategory) => {
class="w-full aspect-video object-cover"
/>
<div
class="absolute inset-x-0 bottom-0 p-3 pt-6 flex justify-between items-end bg-gradient-to-t from-black/50 to-transparent"
class="absolute inset-x-0 bottom-0 p-3 pt-6 flex justify-between items-end bg-linear-to-t from-black/50 to-transparent"
>
<div class="space-y-0.5">
<h3 class="text-base font-bold text-white">{{ ppt.title }}</h3>

View File

@@ -608,7 +608,7 @@ onMounted(() => {
<UButton
color="gray"
variant="ghost"
class="!text-gray-500"
class="text-gray-500!"
@click="
() => {
router.push('/user/register')

View File

@@ -157,7 +157,7 @@ const onSubmit = (form: RegisterSchema) => {
<UButton
color="gray"
variant="ghost"
class="!text-gray-500"
class="text-gray-500!"
@click="
() => {
router.push('/user/authenticate')

View File

@@ -53,11 +53,11 @@
"oxfmt": "^0.28.0",
"oxlint": "^1.43.0",
"sass": "^1.77.8",
"tailwindcss": "^4.1.18",
"typescript": "^5.9.3"
},
"peerDependencies": {
"dayjs": "^1.11.19",
"tailwindcss": "^3.4.7"
"dayjs": "^1.11.19"
},
"packageManager": "pnpm@10.22.0"
}

19
pnpm-lock.yaml generated
View File

@@ -74,9 +74,6 @@ importers:
radix-vue:
specifier: ^1.9.2
version: 1.9.17(vue@3.5.28(typescript@5.9.3))
tailwindcss:
specifier: ^3.4.7
version: 3.4.19(yaml@2.8.2)
v-calendar:
specifier: ^3.1.2
version: 3.1.2(@popperjs/core@2.11.8)(vue@3.5.28(typescript@5.9.3))
@@ -101,7 +98,7 @@ importers:
version: 0.11.3(magicast@0.5.2)(pinia@2.3.1(typescript@5.9.3)(vue@3.5.28(typescript@5.9.3)))
'@tailwindcss/typography':
specifier: ^0.5.13
version: 0.5.19(tailwindcss@3.4.19(yaml@2.8.2))
version: 0.5.19(tailwindcss@4.1.18)
'@types/markdown-it':
specifier: ^13.0.9
version: 13.0.9
@@ -129,6 +126,9 @@ importers:
sass:
specifier: ^1.77.8
version: 1.97.3
tailwindcss:
specifier: ^4.1.18
version: 4.1.18
typescript:
specifier: ^5.9.3
version: 5.9.3
@@ -3180,6 +3180,7 @@ packages:
glob@11.1.0:
resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==}
engines: {node: 20 || >=22}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
glob@13.0.1:
@@ -4882,6 +4883,9 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
tailwindcss@4.1.18:
resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==}
tar-fs@2.1.4:
resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
@@ -7544,6 +7548,11 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 3.4.19(yaml@2.8.2)
'@tailwindcss/typography@0.5.19(tailwindcss@4.1.18)':
dependencies:
postcss-selector-parser: 6.0.10
tailwindcss: 4.1.18
'@tanstack/virtual-core@3.13.18': {}
'@tanstack/vue-virtual@3.13.18(vue@3.5.28(typescript@5.9.3))':
@@ -11031,6 +11040,8 @@ snapshots:
- tsx
- yaml
tailwindcss@4.1.18: {}
tar-fs@2.1.4:
dependencies:
chownr: 1.1.4