mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-12 23:33:43 +08:00
feat: upgrade Liquid Glass design system to full spec (v4.6.0)
- Upgrade CSS variables with proper glass transparency, fixed gradient backgrounds, cubic-bezier(0.2, 0.8, 0.2, 1) fluid transitions, and enhanced shadow/border values matching the Liquid Glass touchstone - Add full backdrop-filter: blur(25px) saturate(180%) to glass-card, glass-input, glass-popover, and glass-backdrop CSS classes - Add lensing inner-glow hover effects and toast-in animation - Update all UI components (Button, Card, Input, Badge, Switch, SegmentedControl, ConfirmDialog, ModalBackdrop, ModalHeader, BackToTop, Navbar) for strict Liquid Glass compliance - Enforce rounded-2xl / rounded-full consistency across all components - Bump version to 4.6.0, update @vercel/analytics to ^2.0.1 Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
b424047c5b
commit
df137c97f3
@@ -10,6 +10,7 @@
|
||||
|
||||
**在线体验:[https://kvideo.pages.dev/](https://kvideo.pages.dev/)**
|
||||
|
||||
[](https://github.com/KuekHaoYang/KVideo)
|
||||
[](https://nextjs.org/)
|
||||
[](https://react.dev/)
|
||||
[](https://tailwindcss.com/)
|
||||
@@ -24,11 +25,12 @@
|
||||
|
||||
项目的视觉设计基于 **"Liquid Glass"** 设计系统,这是一套融合了以下特性的现代化 UI 设计语言:
|
||||
|
||||
- **玻璃拟态效果**:通过 `backdrop-filter` 实现的磨砂半透明效果,让 UI 元素如同真实的玻璃材质
|
||||
- **通用柔和度**:统一使用 `rounded-2xl` 和 `rounded-full` 两种圆角半径,创造和谐的视觉体验
|
||||
- **光影交互**:悬停和聚焦状态下的内发光效果,模拟光线被"捕获"的物理现象
|
||||
- **流畅动画**:基于物理的 `cubic-bezier` 曲线,实现自然的加速和减速过渡
|
||||
- **深度层级**:清晰的 z-axis 层次结构,增强空间感和交互反馈
|
||||
- **玻璃拟态效果**:通过 `backdrop-filter: blur(25px) saturate(180%)` 实现的磨砂半透明效果,让 UI 元素如同真实的玻璃材质,动态反射和折射背景内容
|
||||
- **通用柔和度**:严格使用 `rounded-2xl`(1.5rem)和 `rounded-full`(9999px)两种圆角半径,创造和谐统一的视觉体验
|
||||
- **光影交互**:悬停和聚焦状态下的内发光效果(lensing),模拟光线被"捕获"的物理现象,配合动态阴影响应用户交互
|
||||
- **流畅动画**:基于物理的 `cubic-bezier(0.2, 0.8, 0.2, 1)` 曲线,实现自然的加速和减速过渡
|
||||
- **深度层级**:清晰的 z-axis 层次结构,主交互层始终在视觉最前方,背景元素通过更多模糊和更低饱和度自然后退
|
||||
- **固定渐变背景**:`background-attachment: fixed` 的渐变背景,让玻璃效果在滚动时保持一致的视觉效果
|
||||
|
||||
## 核心功能
|
||||
|
||||
|
||||
+27
-54
@@ -1,71 +1,38 @@
|
||||
/* Effects - Liquid Glass Design System */
|
||||
@keyframes pulse {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes spin-slow {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes spin-reverse {
|
||||
from {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
from { transform: rotate(360deg); }
|
||||
to { transform: rotate(0deg); }
|
||||
}
|
||||
|
||||
@keyframes bounce-subtle {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-5px); }
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
0%, 100% {
|
||||
transform: translateY(0) translateX(0);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-20px) translateX(10px);
|
||||
opacity: 0.8;
|
||||
@@ -73,13 +40,19 @@
|
||||
}
|
||||
|
||||
@keyframes gradient-x {
|
||||
0%, 100% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
}
|
||||
|
||||
0%,
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
/* Lensing inner-glow effect for interactive elements */
|
||||
.glass-glow:hover {
|
||||
box-shadow:
|
||||
0 8px 24px var(--shadow-color),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
}
|
||||
.glass-glow:focus-visible {
|
||||
box-shadow:
|
||||
0 0 0 3px color-mix(in srgb, var(--accent-color) 30%, transparent),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
+50
-12
@@ -1,36 +1,42 @@
|
||||
/* Glass Components */
|
||||
/* Glass Components - Liquid Glass Design System */
|
||||
.glass-card {
|
||||
background: var(--bg-color);
|
||||
opacity: 1;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(25px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(25px) saturate(180%);
|
||||
border-radius: var(--radius-2xl);
|
||||
box-shadow: var(--shadow-sm);
|
||||
box-shadow: var(--shadow-md);
|
||||
border: 1px solid var(--glass-border);
|
||||
transition: transform 0.2s ease-out, box-shadow 0.2s ease-out;
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
transition: all var(--transition-fluid);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.glass-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-5px) scale(1.02);
|
||||
box-shadow: 0 8px 24px var(--shadow-color);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.glass-input {
|
||||
width: 100%;
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(10px) saturate(150%);
|
||||
-webkit-backdrop-filter: blur(10px) saturate(150%);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: var(--radius-2xl);
|
||||
color: var(--text-color);
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
transition: all var(--transition-fluid);
|
||||
}
|
||||
|
||||
.glass-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-color);
|
||||
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.3);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-color) 30%, transparent);
|
||||
}
|
||||
|
||||
.glass-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--accent-color);
|
||||
color: white;
|
||||
border: none;
|
||||
@@ -45,7 +51,7 @@
|
||||
.glass-button:hover {
|
||||
transform: translateY(-2px);
|
||||
filter: brightness(1.1);
|
||||
box-shadow: var(--shadow-md);
|
||||
box-shadow: 0 4px 8px var(--shadow-color);
|
||||
}
|
||||
|
||||
.glass-button:active {
|
||||
@@ -53,6 +59,15 @@
|
||||
filter: brightness(0.95);
|
||||
}
|
||||
|
||||
.glass-button:disabled {
|
||||
background-color: var(--text-color-secondary);
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
filter: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.glass-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -64,3 +79,26 @@
|
||||
background-color: var(--accent-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.glass-badge-secondary {
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* Glass backdrop for modals/drawers */
|
||||
.glass-backdrop {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
/* Glass popover/dropdown */
|
||||
.glass-popover {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(25px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(25px) saturate(180%);
|
||||
border-radius: var(--radius-2xl);
|
||||
box-shadow: var(--shadow-md);
|
||||
border: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
+36
-23
@@ -1,9 +1,9 @@
|
||||
/* Transitions - Liquid Glass Design System */
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
@@ -15,7 +15,6 @@
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
@@ -25,9 +24,8 @@
|
||||
@keyframes scale-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(10px);
|
||||
transform: scale(0.95) translateY(10px);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
@@ -39,10 +37,9 @@
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(-10px);
|
||||
transform: scale(0.95) translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +48,6 @@
|
||||
opacity: 0;
|
||||
transform: translateY(-10px) scale(0.95);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
@@ -59,23 +55,13 @@
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
from { transform: translateX(100%); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
@@ -83,9 +69,36 @@
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes toast-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Utility animation classes */
|
||||
.animate-fade-in {
|
||||
animation: fade-in 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
|
||||
.animate-slide-up {
|
||||
animation: slide-up 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
|
||||
.animate-scale-in {
|
||||
animation: scale-in 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
|
||||
.animate-toast-in {
|
||||
animation: toast-in 0.5s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
|
||||
+19
-17
@@ -1,35 +1,35 @@
|
||||
/* CSS Custom Properties */
|
||||
/* CSS Custom Properties - Liquid Glass Design System */
|
||||
:root {
|
||||
--font-family-system: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif;
|
||||
|
||||
/* Light Mode Palette */
|
||||
--bg-color-light: #f2f4f7;
|
||||
--bg-image-light: none;
|
||||
--bg-color-light: #f0f2f5;
|
||||
--bg-image-light: linear-gradient(120deg, #fdfbfb 0%, #ebedee 100%);
|
||||
--text-color-light: #1d1d1f;
|
||||
--text-color-secondary-light: #6e6e73;
|
||||
--accent-color-light: #0056b3;
|
||||
--glass-bg-light: rgba(255, 255, 255, 0.95);
|
||||
--glass-border-light: rgba(0, 0, 0, 0.05);
|
||||
--shadow-color-light: rgba(0, 0, 0, 0.05);
|
||||
--accent-color-light: #007aff;
|
||||
--glass-bg-light: rgba(242, 242, 247, 0.8);
|
||||
--glass-border-light: rgba(255, 255, 255, 0.5);
|
||||
--shadow-color-light: rgba(0, 0, 0, 0.1);
|
||||
|
||||
/* Dark Mode Palette */
|
||||
--bg-color-dark: #121212;
|
||||
--bg-image-dark: linear-gradient(120deg, #1a1a1a 0%, #121212 100%);
|
||||
--bg-image-dark: linear-gradient(120deg, #272B30 0%, #121212 100%);
|
||||
--text-color-dark: #f5f5f7;
|
||||
--text-color-secondary-dark: #8e8e93;
|
||||
--accent-color-dark: #1A6DBF;
|
||||
--glass-bg-dark: rgba(30, 30, 30, 0.9);
|
||||
--glass-border-dark: rgba(255, 255, 255, 0.1);
|
||||
--accent-color-dark: #0a84ff;
|
||||
--glass-bg-dark: rgba(28, 28, 30, 0.75);
|
||||
--glass-border-dark: rgba(60, 60, 60, 0.7);
|
||||
--shadow-color-dark: rgba(0, 0, 0, 0.3);
|
||||
|
||||
/* Universal Variables */
|
||||
--radius-2xl: 1.5rem;
|
||||
--radius-full: 9999px;
|
||||
--shadow-sm: 0 1px 2px var(--shadow-color);
|
||||
--shadow-md: 0 4px 6px var(--shadow-color);
|
||||
--transition-fluid: 0.3s ease;
|
||||
--shadow-sm: 0 2px 4px var(--shadow-color);
|
||||
--shadow-md: 0 4px 12px var(--shadow-color);
|
||||
--transition-fluid: 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
|
||||
/* Active Theme Variables */
|
||||
/* Active Theme Variables (default: light) */
|
||||
--bg-color: var(--bg-color-light);
|
||||
--bg-image: var(--bg-image-light);
|
||||
--text-color: var(--text-color-light);
|
||||
@@ -39,7 +39,7 @@
|
||||
--glass-border: var(--glass-border-light);
|
||||
--shadow-color: var(--shadow-color-light);
|
||||
|
||||
--background: #f2f4f7;
|
||||
--background: #f0f2f5;
|
||||
--foreground: #1d1d1f;
|
||||
}
|
||||
|
||||
@@ -55,8 +55,10 @@ body {
|
||||
|
||||
background-color: var(--bg-color);
|
||||
background-image: var(--bg-image);
|
||||
background-attachment: fixed;
|
||||
color: var(--text-color);
|
||||
font-family: var(--font-family-system);
|
||||
transition: background 0.3s ease;
|
||||
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior-y: none;
|
||||
@@ -75,4 +77,4 @@ body.dark,
|
||||
|
||||
--background: #121212;
|
||||
--foreground: #f5f5f7;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,19 +24,35 @@ export function Navbar({ onReset, isPremiumMode = false }: NavbarProps) {
|
||||
|
||||
const handleLogout = () => {
|
||||
clearSession();
|
||||
// Navigate to root to clear search query params
|
||||
window.location.href = '/';
|
||||
};
|
||||
|
||||
const iconBtnClass = `
|
||||
w-8 h-8 sm:w-10 sm:h-10
|
||||
flex items-center justify-center
|
||||
rounded-[var(--radius-full)]
|
||||
bg-[var(--glass-bg)]
|
||||
border border-[var(--glass-border)]
|
||||
text-[var(--text-color)]
|
||||
hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]
|
||||
transition-all duration-200
|
||||
cursor-pointer
|
||||
`;
|
||||
|
||||
return (
|
||||
<nav className="sticky top-0 z-[2000] pt-4 pb-2" style={{
|
||||
transform: 'translate3d(0, 0, 0)',
|
||||
willChange: 'transform'
|
||||
}}>
|
||||
<nav className="sticky top-0 z-[2000] pt-4 pb-2">
|
||||
<div className="max-w-7xl mx-auto px-4">
|
||||
<div className="bg-[var(--glass-bg)] border border-[var(--glass-border)] shadow-[var(--shadow-sm)] px-3 sm:px-6 py-2 sm:py-4 rounded-[var(--radius-2xl)]" style={{
|
||||
transform: 'translate3d(0, 0, 0)'
|
||||
}}>
|
||||
<div className="
|
||||
bg-[var(--glass-bg)]
|
||||
backdrop-blur-[25px] saturate-[180%]
|
||||
[-webkit-backdrop-filter:blur(25px)_saturate(180%)]
|
||||
border border-[var(--glass-border)]
|
||||
shadow-[var(--shadow-md)]
|
||||
px-3 sm:px-6 py-2 sm:py-4
|
||||
rounded-[var(--radius-2xl)]
|
||||
transition-all duration-[0.4s]
|
||||
[transition-timing-function:cubic-bezier(0.2,0.8,0.2,1)]
|
||||
">
|
||||
<div className="flex items-center justify-between gap-2 sm:gap-4">
|
||||
<Link
|
||||
href={isPremiumMode ? '/premium' : '/'}
|
||||
@@ -60,20 +76,12 @@ export function Navbar({ onReset, isPremiumMode = false }: NavbarProps) {
|
||||
</Link>
|
||||
|
||||
<div className="flex items-center gap-2 sm:gap-3 flex-shrink-0">
|
||||
{/* IPTV Link - only show if user has iptv_access or no auth configured */}
|
||||
{hasPermission('iptv_access') && (
|
||||
<Link
|
||||
href="/iptv"
|
||||
className="w-8 h-8 sm:w-10 sm:h-10 flex items-center justify-center rounded-[var(--radius-full)] bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)] transition-all duration-200 cursor-pointer"
|
||||
aria-label="直播"
|
||||
title="直播"
|
||||
data-focusable
|
||||
>
|
||||
<Icons.TV size={16} className="sm:w-5 sm:h-5" />
|
||||
</Link>
|
||||
<Link href="/iptv" className={iconBtnClass} aria-label="直播" title="直播" data-focusable>
|
||||
<Icons.TV size={16} className="sm:w-5 sm:h-5" />
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{/* User Info */}
|
||||
{session && (
|
||||
<div className="hidden sm:flex items-center gap-2">
|
||||
<div className="flex items-center gap-1.5 px-2.5 py-1 bg-[var(--glass-bg)] border border-[var(--glass-border)] rounded-[var(--radius-full)] text-xs">
|
||||
@@ -89,7 +97,7 @@ export function Navbar({ onReset, isPremiumMode = false }: NavbarProps) {
|
||||
</div>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="w-8 h-8 sm:w-8 sm:h-8 flex items-center justify-center rounded-[var(--radius-full)] bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color-secondary)] hover:text-red-500 hover:border-red-500/30 transition-all duration-200 cursor-pointer"
|
||||
className="w-8 h-8 flex items-center justify-center rounded-[var(--radius-full)] bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color-secondary)] hover:text-red-500 hover:border-red-500/30 transition-all duration-200 cursor-pointer"
|
||||
aria-label="退出登录"
|
||||
title="退出登录"
|
||||
>
|
||||
@@ -101,17 +109,12 @@ export function Navbar({ onReset, isPremiumMode = false }: NavbarProps) {
|
||||
href="https://github.com/KuekHaoYang/KVideo"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-8 h-8 sm:w-10 sm:h-10 flex items-center justify-center rounded-[var(--radius-full)] bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)] transition-all duration-200 cursor-pointer hidden sm:flex"
|
||||
className={`${iconBtnClass} hidden sm:flex`}
|
||||
aria-label="GitHub 仓库"
|
||||
>
|
||||
<Icons.Github size={20} />
|
||||
</a>
|
||||
<Link
|
||||
href={settingsHref}
|
||||
className="w-8 h-8 sm:w-10 sm:h-10 flex items-center justify-center rounded-[var(--radius-full)] bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)] transition-all duration-200 cursor-pointer"
|
||||
aria-label="设置"
|
||||
data-focusable
|
||||
>
|
||||
<Link href={settingsHref} className={iconBtnClass} aria-label="设置" data-focusable>
|
||||
<svg className="w-4 h-4 sm:w-5 sm:h-5" viewBox="0 -960 960 960" fill="currentColor">
|
||||
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z" />
|
||||
</svg>
|
||||
|
||||
@@ -38,11 +38,17 @@ export function BackToTop() {
|
||||
return (
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
className={`fixed bottom-8 right-8 z-[9999] p-3 rounded-full
|
||||
bg-[var(--glass-bg)] border border-[var(--glass-border)]
|
||||
shadow-[var(--shadow-md)] backdrop-blur-xl
|
||||
text-[var(--text-color)] transition-all duration-300 ease-out
|
||||
hover:bg-[color-mix(in_srgb,var(--accent-color)_15%,transparent)]
|
||||
className={`fixed bottom-8 right-8 z-[9999] p-3
|
||||
rounded-[var(--radius-full)]
|
||||
bg-[var(--glass-bg)]
|
||||
backdrop-blur-[25px] saturate-[180%]
|
||||
[-webkit-backdrop-filter:blur(25px)_saturate(180%)]
|
||||
border border-[var(--glass-border)]
|
||||
shadow-[var(--shadow-md)]
|
||||
text-[var(--text-color)]
|
||||
transition-all duration-[0.4s]
|
||||
[transition-timing-function:cubic-bezier(0.2,0.8,0.2,1)]
|
||||
hover:bg-[color-mix(in_srgb,var(--accent-color)_15%,transparent)]
|
||||
hover:scale-110 active:scale-95
|
||||
${isVisible
|
||||
? 'opacity-100 translate-y-0 scale-100'
|
||||
|
||||
+2
-15
@@ -22,14 +22,8 @@ const BadgeComponent = memo(function Badge({
|
||||
|
||||
const iconElement = icon && (
|
||||
<span
|
||||
className={`inline-flex items-center justify-center ${iconPosition === 'left' ? 'mr-1' : 'ml-1'
|
||||
}`}
|
||||
style={{
|
||||
width: '0.875em',
|
||||
height: '0.875em',
|
||||
transform: 'translateZ(0)',
|
||||
willChange: 'auto',
|
||||
}}
|
||||
className={`inline-flex items-center justify-center ${iconPosition === 'left' ? 'mr-1' : 'ml-1'}`}
|
||||
style={{ width: '0.875em', height: '0.875em' }}
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
@@ -45,10 +39,6 @@ const BadgeComponent = memo(function Badge({
|
||||
${variants[variant]}
|
||||
${className}
|
||||
`}
|
||||
style={{
|
||||
transform: 'translateZ(0)',
|
||||
willChange: 'auto',
|
||||
}}
|
||||
>
|
||||
{icon && iconPosition === 'left' && iconElement}
|
||||
{children}
|
||||
@@ -57,7 +47,4 @@ const BadgeComponent = memo(function Badge({
|
||||
);
|
||||
});
|
||||
|
||||
// Export both named and default for compatibility
|
||||
export const Badge = BadgeComponent;
|
||||
|
||||
|
||||
|
||||
+23
-19
@@ -11,31 +11,37 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(({
|
||||
className = '',
|
||||
...props
|
||||
}, ref) => {
|
||||
const baseStyles = "inline-flex items-center justify-center px-4 py-2.5 md:px-6 md:py-3 font-semibold text-sm md:text-base transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed min-h-[44px] touch-manipulation cursor-pointer";
|
||||
const baseStyles = `
|
||||
inline-flex items-center justify-center
|
||||
px-4 py-2.5 md:px-6 md:py-3
|
||||
font-semibold text-sm md:text-base
|
||||
rounded-[var(--radius-2xl)]
|
||||
transition-all duration-200
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
disabled:transform-none disabled:filter-none disabled:shadow-none
|
||||
min-h-[44px] touch-manipulation cursor-pointer
|
||||
`;
|
||||
|
||||
const variants = {
|
||||
primary: `
|
||||
bg-[var(--accent-color)]
|
||||
text-white
|
||||
border-none
|
||||
rounded-[var(--radius-2xl)]
|
||||
shadow-[0_2px_8px_color-mix(in_srgb,var(--shadow-color)_50%,transparent)]
|
||||
hover:brightness-110
|
||||
hover:shadow-[0_4px_12px_color-mix(in_srgb,var(--shadow-color)_70%,transparent)]
|
||||
active:scale-[0.98]
|
||||
bg-[var(--accent-color)]
|
||||
text-white
|
||||
border-none
|
||||
shadow-[var(--shadow-sm)]
|
||||
hover:translate-y-[-2px]
|
||||
hover:brightness-110
|
||||
hover:shadow-[0_4px_8px_var(--shadow-color)]
|
||||
active:translate-y-0 active:scale-[0.98]
|
||||
active:brightness-95
|
||||
`,
|
||||
secondary: `
|
||||
bg-[var(--glass-bg)]
|
||||
backdrop-blur-xl
|
||||
bg-[var(--glass-bg)]
|
||||
backdrop-blur-[25px] saturate-[180%]
|
||||
[-webkit-backdrop-filter:blur(25px)_saturate(180%)]
|
||||
saturate-[180%]
|
||||
border
|
||||
border-[var(--glass-border)]
|
||||
rounded-[var(--radius-2xl)]
|
||||
border border-[var(--glass-border)]
|
||||
text-[var(--text-color)]
|
||||
shadow-[0_2px_8px_color-mix(in_srgb,var(--shadow-color)_50%,transparent)]
|
||||
hover:shadow-[0_4px_12px_color-mix(in_srgb,var(--shadow-color)_70%,transparent)]
|
||||
shadow-[var(--shadow-sm)]
|
||||
hover:shadow-[0_4px_8px_var(--shadow-color)]
|
||||
active:scale-[0.98]
|
||||
`,
|
||||
ghost: `
|
||||
@@ -58,5 +64,3 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(({
|
||||
});
|
||||
|
||||
Button.displayName = 'Button';
|
||||
|
||||
|
||||
|
||||
@@ -11,27 +11,26 @@ interface CardProps {
|
||||
|
||||
export function Card({ children, className = '', hover = true, blur = true, onClick, style }: CardProps) {
|
||||
const hoverStyles = hover
|
||||
? "hover:translate-y-[-2px] hover:shadow-[0_8px_24px_var(--shadow-color)] cursor-pointer transition-transform duration-200 ease-out"
|
||||
? "hover:translate-y-[-5px] hover:scale-[1.02] hover:shadow-[0_8px_24px_var(--shadow-color)] hover:z-10 cursor-pointer"
|
||||
: "";
|
||||
|
||||
// Conditionally apply blur classes - NOW DISABLED GLOBALLY via CSS override, but logic kept for structure
|
||||
const blurClasses = blur
|
||||
? "bg-[var(--glass-bg)]" // Removed backdrop-blur classes here as they are expensive
|
||||
: "bg-[var(--bg-color)]/90"; // More opaque fallback
|
||||
? "bg-[var(--glass-bg)] backdrop-blur-[25px] saturate-[180%] [-webkit-backdrop-filter:blur(25px)_saturate(180%)]"
|
||||
: "bg-[var(--bg-color)]/90";
|
||||
|
||||
const baseClasses = `
|
||||
${blurClasses}
|
||||
rounded-[var(--radius-2xl)]
|
||||
shadow-[0_2px_8px_var(--shadow-color)] md:shadow-[var(--shadow-md)]
|
||||
shadow-[var(--shadow-md)]
|
||||
border
|
||||
border-[var(--glass-border)]
|
||||
p-4 md:p-6
|
||||
relative
|
||||
transition-all duration-[0.4s] [transition-timing-function:cubic-bezier(0.2,0.8,0.2,1)]
|
||||
${hoverStyles}
|
||||
${className}
|
||||
`;
|
||||
|
||||
// Use semantic button when interactive
|
||||
if (onClick) {
|
||||
return (
|
||||
<button
|
||||
@@ -45,12 +44,9 @@ export function Card({ children, className = '', hover = true, blur = true, onCl
|
||||
);
|
||||
}
|
||||
|
||||
// Use div for non-interactive cards
|
||||
return (
|
||||
<div className={baseClasses} style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,36 +27,26 @@ export function ConfirmDialog({
|
||||
variant = 'warning',
|
||||
dangerous = false,
|
||||
}: ConfirmDialogProps) {
|
||||
const dialogRef = useRef<HTMLDivElement>(null);
|
||||
const cancelButtonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
// Focus the cancel button when dialog opens
|
||||
cancelButtonRef.current?.focus();
|
||||
|
||||
// Prevent body scroll
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
// Restore body scroll
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.body.style.overflow = '';
|
||||
};
|
||||
return () => { document.body.style.overflow = ''; };
|
||||
}, [isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
onCancel();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
return () => document.removeEventListener('keydown', handleKeyDown);
|
||||
}, [isOpen, onCancel]);
|
||||
@@ -68,46 +58,39 @@ export function ConfirmDialog({
|
||||
warning: 'bg-[var(--accent-color)] hover:brightness-110',
|
||||
info: 'bg-blue-500 hover:bg-blue-600',
|
||||
};
|
||||
|
||||
// Use dangerous prop to override variant
|
||||
|
||||
const finalVariant = dangerous ? 'danger' : variant;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Backdrop */}
|
||||
<div
|
||||
className="fixed inset-0 z-[9998] bg-black/30 backdrop-blur-sm animate-fade-in"
|
||||
className="fixed inset-0 z-[9998] bg-black/30 backdrop-blur-[8px] [-webkit-backdrop-filter:blur(8px)] animate-fade-in"
|
||||
onClick={onCancel}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
{/* Dialog */}
|
||||
<div
|
||||
ref={dialogRef}
|
||||
role="alertdialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="dialog-title"
|
||||
aria-describedby="dialog-description"
|
||||
className="fixed top-1/2 left-1/2 z-[9999] w-[90%] max-w-md -translate-x-1/2 -translate-y-1/2 animate-slide-up"
|
||||
className="fixed top-1/2 left-1/2 z-[9999] w-[90%] max-w-md -translate-x-1/2 -translate-y-1/2 animate-scale-in"
|
||||
>
|
||||
<Card className="p-6">
|
||||
{/* Header */}
|
||||
<Card hover={false} className="p-6">
|
||||
<h2
|
||||
id="dialog-title"
|
||||
className="text-xl font-semibold text-[var(--text-color)] mb-3"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
|
||||
{/* Message */}
|
||||
<p
|
||||
id="dialog-description"
|
||||
className="text-[var(--text-color-secondary)] mb-6 leading-relaxed"
|
||||
>
|
||||
{message}
|
||||
</p>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-3 justify-end">
|
||||
<Button
|
||||
ref={cancelButtonRef}
|
||||
|
||||
@@ -18,21 +18,19 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
ref={ref}
|
||||
className={`
|
||||
w-full px-4 py-3 md:px-6 md:py-4
|
||||
text-base md:text-[var(--text-color)]
|
||||
text-base
|
||||
bg-[var(--glass-bg)]
|
||||
backdrop-blur-[10px]
|
||||
saturate-[150%]
|
||||
backdrop-blur-[10px] saturate-[150%]
|
||||
[-webkit-backdrop-filter:blur(10px)_saturate(150%)]
|
||||
border
|
||||
border-[var(--glass-border)]
|
||||
border border-[var(--glass-border)]
|
||||
rounded-[var(--radius-2xl)]
|
||||
text-[var(--text-color)]
|
||||
placeholder:text-[var(--text-color-secondary)]
|
||||
focus:outline-none
|
||||
focus:border-[var(--accent-color)]
|
||||
focus:shadow-[0_0_0_3px_color-mix(in_srgb,var(--accent-color)_30%,transparent)]
|
||||
transition-all
|
||||
duration-[var(--transition-fluid)]
|
||||
transition-all duration-[0.4s]
|
||||
[transition-timing-function:cubic-bezier(0.2,0.8,0.2,1)]
|
||||
touch-manipulation
|
||||
${error ? 'border-red-500' : ''}
|
||||
${className}
|
||||
@@ -48,4 +46,3 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
);
|
||||
|
||||
Input.displayName = 'Input';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Reusable modal backdrop component
|
||||
* Reusable modal backdrop component - Liquid Glass design
|
||||
*/
|
||||
|
||||
interface ModalBackdropProps {
|
||||
@@ -10,9 +10,16 @@ interface ModalBackdropProps {
|
||||
export function ModalBackdrop({ isOpen, onClose }: ModalBackdropProps) {
|
||||
return (
|
||||
<div
|
||||
className={`fixed inset-0 z-[9998] bg-black/30 backdrop-blur-md transition-opacity duration-300 ${isOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'
|
||||
}`}
|
||||
className={`
|
||||
fixed inset-0 z-[9998]
|
||||
bg-black/30
|
||||
backdrop-blur-[8px]
|
||||
[-webkit-backdrop-filter:blur(8px)]
|
||||
transition-opacity duration-300
|
||||
${isOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'}
|
||||
`}
|
||||
onClick={onClose}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Reusable modal header component
|
||||
* Reusable modal header component - Liquid Glass design
|
||||
*/
|
||||
|
||||
interface ModalHeaderProps {
|
||||
@@ -15,7 +15,16 @@ export function ModalHeader({ title, onClose }: ModalHeaderProps) {
|
||||
</h3>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="w-8 h-8 flex items-center justify-center rounded-[var(--radius-full)] bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)] transition-all duration-200 cursor-pointer"
|
||||
className="
|
||||
w-8 h-8 flex items-center justify-center
|
||||
rounded-[var(--radius-full)]
|
||||
bg-[var(--glass-bg)]
|
||||
border border-[var(--glass-border)]
|
||||
text-[var(--text-color)]
|
||||
hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)]
|
||||
transition-all duration-200
|
||||
cursor-pointer
|
||||
"
|
||||
aria-label="关闭"
|
||||
>
|
||||
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
|
||||
@@ -38,7 +38,6 @@ export function SegmentedControl<T extends string>({
|
||||
};
|
||||
|
||||
updateIndicator();
|
||||
// Update on window resize as well
|
||||
window.addEventListener('resize', updateIndicator);
|
||||
return () => window.removeEventListener('resize', updateIndicator);
|
||||
}, [value, options]);
|
||||
@@ -47,14 +46,19 @@ export function SegmentedControl<T extends string>({
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={`
|
||||
relative flex p-1 bg-[var(--glass-bg)] backdrop-blur-xl
|
||||
border border-[var(--glass-border)] rounded-[var(--radius-2xl)]
|
||||
shadow-[var(--shadow-sm)] ${className}
|
||||
relative flex p-1
|
||||
bg-[var(--glass-bg)]
|
||||
backdrop-blur-[25px] saturate-[180%]
|
||||
[-webkit-backdrop-filter:blur(25px)_saturate(180%)]
|
||||
border border-[var(--glass-border)]
|
||||
rounded-[var(--radius-2xl)]
|
||||
shadow-[var(--shadow-sm)]
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
{/* Sliding Indicator */}
|
||||
<div
|
||||
className="absolute top-1 bottom-1 bg-[var(--accent-color)] rounded-[calc(var(--radius-2xl)-4px)] shadow-[0_2px_8px_rgba(0,122,255,0.3)] transition-all duration-300 cubic-bezier(0.2, 0.8, 0.2, 1)"
|
||||
className="absolute top-1 bottom-1 bg-[var(--accent-color)] rounded-[calc(var(--radius-2xl)-4px)] shadow-[0_2px_8px_color-mix(in_srgb,var(--accent-color)_30%,transparent)] transition-all duration-300 [transition-timing-function:cubic-bezier(0.2,0.8,0.2,1)]"
|
||||
style={{
|
||||
left: `${indicatorStyle.left}px`,
|
||||
width: `${indicatorStyle.width}px`,
|
||||
@@ -68,8 +72,13 @@ export function SegmentedControl<T extends string>({
|
||||
data-value={option.value}
|
||||
onClick={() => onChange(option.value)}
|
||||
className={`
|
||||
relative z-10 flex-1 py-2 px-4 text-sm font-semibold transition-colors duration-200
|
||||
${value === option.value ? 'text-white' : 'text-[var(--text-color-secondary)] hover:text-[var(--text-color)]'}
|
||||
relative z-10 flex-1 py-2 px-4
|
||||
text-sm font-semibold
|
||||
transition-colors duration-200
|
||||
${value === option.value
|
||||
? 'text-white'
|
||||
: 'text-[var(--text-color-secondary)] hover:text-[var(--text-color)]'
|
||||
}
|
||||
`}
|
||||
>
|
||||
{option.label}
|
||||
|
||||
+21
-19
@@ -25,11 +25,11 @@ export function Switch({
|
||||
return (
|
||||
<label
|
||||
className={`
|
||||
switch relative inline-flex items-center cursor-pointer
|
||||
h-[30px] w-[50px] shrink-0
|
||||
${disabled ? 'opacity-50 cursor-not-allowed' : ''}
|
||||
${className}
|
||||
`}
|
||||
relative inline-flex items-center cursor-pointer
|
||||
h-[30px] w-[50px] shrink-0
|
||||
${disabled ? 'opacity-50 cursor-not-allowed' : ''}
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -41,20 +41,22 @@ export function Switch({
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
switch-slider w-full h-full rounded-[var(--radius-full)]
|
||||
bg-[color-mix(in_srgb,var(--text-color)_20%,transparent)]
|
||||
peer-checked:bg-[var(--accent-color)]
|
||||
transition-colors duration-[0.4s] cubic-bezier(0.2,0.8,0.2,1)
|
||||
before:content-[''] before:absolute before:h-[26px] before:w-[26px]
|
||||
before:left-[2px] before:bottom-[2px]
|
||||
before:bg-white before:rounded-[var(--radius-full)]
|
||||
before:transition-transform before:duration-[0.4s]
|
||||
before:cubic-bezier(0.2,0.8,0.2,1)
|
||||
before:shadow-[0_1px_3px_rgba(0,0,0,0.2)]
|
||||
peer-checked:before:translate-x-[20px]
|
||||
active:before:scale-95
|
||||
`}
|
||||
></div>
|
||||
w-full h-full rounded-[var(--radius-full)]
|
||||
bg-[color-mix(in_srgb,var(--text-color)_20%,transparent)]
|
||||
peer-checked:bg-[var(--accent-color)]
|
||||
transition-[background-color] duration-[0.4s]
|
||||
[transition-timing-function:cubic-bezier(0.2,0.8,0.2,1)]
|
||||
before:content-[''] before:absolute
|
||||
before:h-[26px] before:w-[26px]
|
||||
before:left-[2px] before:bottom-[2px]
|
||||
before:bg-white before:rounded-[var(--radius-full)]
|
||||
before:transition-transform before:duration-[0.4s]
|
||||
before:[transition-timing-function:cubic-bezier(0.2,0.8,0.2,1)]
|
||||
before:shadow-[0_1px_3px_rgba(0,0,0,0.2)]
|
||||
peer-checked:before:translate-x-[20px]
|
||||
active:before:scale-95
|
||||
`}
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
Generated
+12
-9
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "kvideo",
|
||||
"version": "4.5.0",
|
||||
"version": "4.6.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "kvideo",
|
||||
"version": "4.5.0",
|
||||
"version": "4.6.0",
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@upstash/redis": "^1.37.0",
|
||||
"@vercel/analytics": "^2.0.0",
|
||||
"@vercel/analytics": "^2.0.1",
|
||||
"hls.js": "^1.6.15",
|
||||
"lucide-react": "^0.577.0",
|
||||
"next": "16.1.6",
|
||||
@@ -3507,9 +3507,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/analytics": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-2.0.0.tgz",
|
||||
"integrity": "sha512-fP/ASXXz+1K/C2vWTnocd8RsGnkO9f1qOIDrhgQ3DagJtnea1EsM9AV9fDzjXlPIPb2vBQapxOIMCjtGIW8PZw==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-2.0.1.tgz",
|
||||
"integrity": "sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@remix-run/react": "^2",
|
||||
@@ -3531,6 +3531,9 @@
|
||||
"next": {
|
||||
"optional": true
|
||||
},
|
||||
"nuxt": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
@@ -11063,9 +11066,9 @@
|
||||
}
|
||||
},
|
||||
"@vercel/analytics": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-2.0.0.tgz",
|
||||
"integrity": "sha512-fP/ASXXz+1K/C2vWTnocd8RsGnkO9f1qOIDrhgQ3DagJtnea1EsM9AV9fDzjXlPIPb2vBQapxOIMCjtGIW8PZw=="
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-2.0.1.tgz",
|
||||
"integrity": "sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g=="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.16.0",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kvideo",
|
||||
"version": "4.5.0",
|
||||
"version": "4.6.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --port ${PORT:-3000}",
|
||||
@@ -14,7 +14,7 @@
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@upstash/redis": "^1.37.0",
|
||||
"@vercel/analytics": "^2.0.0",
|
||||
"@vercel/analytics": "^2.0.1",
|
||||
"hls.js": "^1.6.15",
|
||||
"lucide-react": "^0.577.0",
|
||||
"next": "16.1.6",
|
||||
|
||||
Reference in New Issue
Block a user