Compare commits

3 Commits

25 changed files with 369 additions and 2432 deletions
+45
View File
@@ -0,0 +1,45 @@
name: CI - Build & Test
run-name: 🔍 CI ${{ gitea.ref_name }} by @${{ gitea.actor }}
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint:
name: Lint & Build
runs-on: linux
steps:
- name: Checkout
uses: actions/checkout@v4
- name: HTML Validate
run: |
echo "Checking HTML structure..."
if grep -q "<!DOCTYPE html>" index.html; then
echo "✅ HTML5 doctype present"
else
echo "❌ Missing HTML5 doctype"
exit 1
fi
- name: CSS Check
run: |
echo "Checking CSS..."
if grep -q "@media" css/style.css; then
echo "✅ Responsive styles present"
else
echo "⚠️ No responsive styles found"
fi
- name: Nginx Config Test
run: |
echo "Checking nginx conf..."
docker run --rm -v $PWD/nginx.conf:/tmp/nginx.conf:ro nginx:1.27-alpine nginx -t -c /tmp/nginx.conf 2>&1 || true
- name: Build Docker Image
run: |
docker build -t noveria-landing:ci .
echo "✅ Build successful"
-4
View File
@@ -1,4 +0,0 @@
node_modules
dist
html
*.local
Executable
+5
View File
@@ -0,0 +1,5 @@
FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY index.html /usr/share/nginx/html/
COPY css/ /usr/share/nginx/html/css/
EXPOSE 80
Executable
+3
View File
@@ -0,0 +1,3 @@
# noveria-landing
Noveria.net Landingpage
-32
View File
@@ -1,32 +0,0 @@
name: bao-landing
services:
web:
image: nginx:alpine
restart: unless-stopped
deploy:
resources:
limits:
memory: 64M
reservations:
memory: 16M
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.bao-landing.rule=Host(`noveria.net`)"
- "traefik.http.routers.bao-landing.tls=true"
- "traefik.http.routers.bao-landing.tls.certresolver=letsencrypt"
- "traefik.http.services.bao-landing.loadbalancer.server.port=80"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
proxy:
external: true
Executable
+264
View File
@@ -0,0 +1,264 @@
/* ═══════════════════════════════════════════════
Noveria Operation Landing Galaxy + Aya Design
Dark Premium: Monochrome + Violett-Akzent
═══════════════════════════════════════════════ */
:root {
--bg: #080a0f;
--tx: #ffffff;
--tx-2: #9ca3af;
--tx-3: #6b7280;
--accent: #8b5cf6;
--accent-strong: #a78bfa;
--accent-glow: rgba(139, 92, 246, .35);
--grad: linear-gradient(135deg, #7c3aed, #a855f7, #6366f1);
--holographic: linear-gradient(135deg, rgba(249,115,22,.06), rgba(236,72,153,.06), rgba(99,102,241,.06));
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
height: 100%;
overflow: hidden;
font-family: 'Space Grotesk', Inter, ui-sans-serif, system-ui, -apple-system, sans-serif;
color: var(--tx);
background: var(--bg);
}
/* ═══ Galaxy Background ═══ */
.galaxy {
position: fixed;
inset: 0;
z-index: 0;
background:
radial-gradient(80% 60% at 50% 40%, rgba(139, 92, 246, .10), transparent 60%),
radial-gradient(60% 50% at 80% 80%, rgba(168, 85, 247, .06), transparent 55%),
radial-gradient(50% 40% at 20% 20%, rgba(99, 102, 241, .05), transparent 50%),
var(--bg);
}
/* Holographic premium gradient */
.holographic {
position: absolute;
inset: 0;
background: var(--holographic);
mix-blend-mode: screen;
pointer-events: none;
}
.stars, .stars2, .stars3 {
position: absolute;
inset: 0;
}
.stars {
background-image:
radial-gradient(1px 1px at 10% 15%, rgba(255,255,255,.8), transparent),
radial-gradient(1px 1px at 25% 45%, rgba(255,255,255,.6), transparent),
radial-gradient(1.5px 1.5px at 40% 10%, rgba(200,190,255,.9), transparent),
radial-gradient(1px 1px at 55% 70%, rgba(255,255,255,.7), transparent),
radial-gradient(1px 1px at 70% 30%, rgba(255,255,255,.5), transparent),
radial-gradient(1.5px 1.5px at 85% 55%, rgba(180,170,255,.8), transparent),
radial-gradient(1px 1px at 15% 80%, rgba(255,255,255,.6), transparent),
radial-gradient(1px 1px at 60% 85%, rgba(255,255,255,.4), transparent),
radial-gradient(1px 1px at 90% 20%, rgba(255,255,255,.7), transparent);
}
.stars2 {
background-image:
radial-gradient(1px 1px at 5% 25%, rgba(255,255,255,.9), transparent),
radial-gradient(1.5px 1.5px at 20% 60%, rgba(220,210,255,.8), transparent),
radial-gradient(1px 1px at 35% 35%, rgba(255,255,255,.5), transparent),
radial-gradient(1px 1px at 50% 15%, rgba(255,255,255,.6), transparent),
radial-gradient(1px 1px at 65% 55%, rgba(255,255,255,.7), transparent),
radial-gradient(1.5px 1.5px at 80% 75%, rgba(200,190,255,.8), transparent),
radial-gradient(1px 1px at 95% 40%, rgba(255,255,255,.5), transparent),
radial-gradient(1px 1px at 45% 90%, rgba(255,255,255,.4), transparent);
animation: twinkle 4s ease-in-out infinite alternate;
}
.stars3 {
background-image:
radial-gradient(1px 1px at 8% 65%, rgba(255,255,255,.6), transparent),
radial-gradient(1px 1px at 18% 15%, rgba(255,255,255,.5), transparent),
radial-gradient(1.5px 1.5px at 30% 80%, rgba(210,200,255,.7), transparent),
radial-gradient(1px 1px at 48% 45%, rgba(255,255,255,.8), transparent),
radial-gradient(1px 1px at 62% 25%, rgba(255,255,255,.4), transparent),
radial-gradient(1px 1px at 75% 65%, rgba(255,255,255,.6), transparent),
radial-gradient(1px 1px at 88% 8%, rgba(255,255,255,.7), transparent),
radial-gradient(1.5px 1.5px at 12% 48%, rgba(190,180,255,.8), transparent);
animation: twinkle 6s ease-in-out infinite alternate-reverse;
}
@keyframes twinkle {
0% { opacity: .5; }
100% { opacity: 1; }
}
/* ═══ Shooting Stars ═══ */
.shooting-star {
position: absolute;
top: -10%;
left: 60%;
width: 2px;
height: 100px;
background: linear-gradient(to bottom, rgba(255,255,255,0), rgba(255,255,255,.9), rgba(139,92,246,.4));
transform: rotate(-30deg);
animation: shoot 4s ease-in infinite;
opacity: 0;
border-radius: 1px;
filter: blur(.5px);
}
.shooting-star:nth-child(5) {
top: -5%;
left: 25%;
animation-delay: 2.5s;
animation-duration: 5s;
height: 70px;
}
@keyframes shoot {
0% { opacity: 0; transform: rotate(-30deg) translate(0, 0); }
2% { opacity: .9; }
8% { opacity: 0; transform: rotate(-30deg) translate(-300px, 600px); }
100% { opacity: 0; transform: rotate(-30deg) translate(-300px, 600px); }
}
/* ═══ Floating Ambient Orbs ═══ */
.orb {
position: fixed;
border-radius: 50%;
filter: blur(80px);
z-index: 0;
pointer-events: none;
}
.orb-1 {
width: 320px; height: 320px;
top: -60px; right: -80px;
background: rgba(139, 92, 246, .12);
animation: float-orb 8s ease-in-out infinite;
}
.orb-2 {
width: 240px; height: 240px;
bottom: -40px; left: -60px;
background: rgba(99, 102, 241, .10);
animation: float-orb 12s ease-in-out infinite reverse;
}
@keyframes float-orb {
0%, 100% { transform: translate(0, 0) scale(1); }
33% { transform: translate(20px, -20px) scale(1.1); }
66% { transform: translate(-10px, 10px) scale(.95); }
}
/* ═══ Content ═══ */
.hero {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: 2rem;
}
.hero-content {
text-align: center;
animation: fade-up .8s ease-out;
}
@keyframes fade-up {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
/* Monogram */
.avatar {
width: 72px; height: 72px;
border-radius: 50%;
margin: 0 auto 1.5rem;
background: rgba(139, 92, 246, .10);
border: 2px solid rgba(139, 92, 246, .25);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.75rem;
font-weight: 700;
color: var(--accent-strong);
letter-spacing: .04em;
box-shadow: 0 0 48px rgba(139, 92, 246, .15);
animation: pulse-avatar 3s ease-in-out infinite;
}
@keyframes pulse-avatar {
0%, 100% { box-shadow: 0 0 32px rgba(139, 92, 246, .10); }
50% { box-shadow: 0 0 56px rgba(139, 92, 246, .20); }
}
/* Headline — EXTREM GROSS */
.hero-title {
font-size: clamp(2.8rem, 9vw, 5.5rem);
font-weight: 700;
letter-spacing: .04em;
background: var(--grad);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
filter: drop-shadow(0 0 32px var(--accent-glow));
margin-bottom: .35rem;
line-height: 1.1;
}
/* Subtext — WINZIG */
.hero-subtitle {
font-size: clamp(.85rem, 2vw, 1.1rem);
color: var(--tx-2);
letter-spacing: .06em;
font-weight: 400;
margin-bottom: 2rem;
}
/* Status-Badge */
.status-badge {
display: inline-block;
padding: .55rem 1.6rem;
border-radius: 99px;
border: 1px solid rgba(139, 92, 246, .30);
background: rgba(139, 92, 246, .08);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
color: var(--accent-strong);
font-size: .8rem;
font-weight: 600;
letter-spacing: .10em;
text-transform: uppercase;
box-shadow: 0 0 36px rgba(139, 92, 246, .15);
margin-bottom: 2.5rem;
animation: pulse-badge 3s ease-in-out infinite;
}
@keyframes pulse-badge {
0%, 100% { box-shadow: 0 0 28px rgba(139, 92, 246, .08); }
50% { box-shadow: 0 0 48px rgba(139, 92, 246, .22); }
}
/* ═══ Footer ═══ */
.footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
text-align: center;
padding: 1rem;
font-size: .7rem;
color: var(--tx-3);
letter-spacing: .04em;
}
/* ═══ Responsive ═══ */
@media (max-width: 640px) {
.hero { padding: 1rem; }
.hero-title { font-size: clamp(2rem, 14vw, 3.5rem); }
.hero-subtitle { font-size: .8rem; }
.avatar { width: 56px; height: 56px; font-size: 1.35rem; }
.orb { display: none; }
.shooting-star { display: none; }
}
Regular → Executable
+37 -10
View File
@@ -1,17 +1,44 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Noveria Operations // Coming Soon</title>
<meta
name="description"
content="Noveria Operations coming soon."
/>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='18' fill='%2303040a'/%3E%3Ccircle cx='50' cy='50' r='33' fill='none' stroke='%237ed8ff' stroke-width='7'/%3E%3Cpath d='M31 61 50 20l19 41H57l-7-17-7 17z' fill='%239d83ff'/%3E%3C/svg%3E" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Noveria Operation Coming Soon</title>
<meta name="description" content="Noveria Operation Tools, Infrastruktur &amp; KI-Lösungen von Minh Bao.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<!-- Galaxy Background -->
<div class="galaxy">
<div class="stars"></div>
<div class="stars2"></div>
<div class="stars3"></div>
<!-- Shooting Stars -->
<div class="shooting-star"></div>
<div class="shooting-star"></div>
<!-- Holographic gradient -->
<div class="holographic"></div>
</div>
<!-- Floating ambient orbs -->
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<!-- Content -->
<main class="hero">
<div class="hero-content">
<div class="avatar">N</div>
<h1 class="hero-title">Noveria Operation</h1>
<p class="hero-subtitle">Tools, Infrastruktur &amp; KI-Lösungen</p>
<div class="status-badge">Coming Soon</div>
</div>
</main>
<footer class="footer">
<p>Built by Minh Bao</p>
</footer>
</body>
</html>
Regular → Executable
+11 -18
View File
@@ -4,25 +4,18 @@ server {
root /usr/share/nginx/html;
index index.html;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml;
gzip_min_length 256;
# Static assets with cache
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# Cache static assets
location /css/ {
expires 7d;
add_header Cache-Control "public, immutable";
}
}
-21
View File
@@ -1,21 +0,0 @@
{
"name": "noveria-landing",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.13"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.3",
"typescript": "~5.7.3",
"vite": "^6.3.1",
"vue-tsc": "^2.2.8"
},
"packageManager": "pnpm@11.7.0+sha512.19cc852c120c7125760f2443ee6be0ca5b40f9f50598de1a09a1f177503e010e57c23c77646e01e761de59bf874fb22a3398c33ab9691fc13eb946b6f0f4d620"
}
-958
View File
@@ -1,958 +0,0 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
vue:
specifier: ^3.5.13
version: 3.5.38(typescript@5.7.3)
devDependencies:
'@vitejs/plugin-vue':
specifier: ^5.2.3
version: 5.2.4(vite@6.4.3)(vue@3.5.38(typescript@5.7.3))
typescript:
specifier: ~5.7.3
version: 5.7.3
vite:
specifier: ^6.3.1
version: 6.4.3
vue-tsc:
specifier: ^2.2.8
version: 2.2.12(typescript@5.7.3)
packages:
'@babel/helper-string-parser@7.29.7':
resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@7.29.7':
resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
engines: {node: '>=6.9.0'}
'@babel/parser@7.29.7':
resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==}
engines: {node: '>=6.0.0'}
hasBin: true
'@babel/types@7.29.7':
resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
engines: {node: '>=6.9.0'}
'@esbuild/aix-ppc64@0.25.12':
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.25.12':
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.25.12':
resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.25.12':
resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.25.12':
resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.25.12':
resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.25.12':
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.25.12':
resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.25.12':
resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.25.12':
resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.25.12':
resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.25.12':
resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.25.12':
resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.25.12':
resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.25.12':
resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.25.12':
resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.25.12':
resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.25.12':
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.25.12':
resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.25.12':
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.25.12':
resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openharmony-arm64@0.25.12':
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.25.12':
resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.25.12':
resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.25.12':
resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.25.12':
resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
'@rollup/rollup-android-arm-eabi@4.62.0':
resolution: {integrity: sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.62.0':
resolution: {integrity: sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.62.0':
resolution: {integrity: sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.62.0':
resolution: {integrity: sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.62.0':
resolution: {integrity: sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.62.0':
resolution: {integrity: sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.62.0':
resolution: {integrity: sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.62.0':
resolution: {integrity: sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.62.0':
resolution: {integrity: sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.62.0':
resolution: {integrity: sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.62.0':
resolution: {integrity: sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.62.0':
resolution: {integrity: sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==}
cpu: [loong64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.62.0':
resolution: {integrity: sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.62.0':
resolution: {integrity: sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==}
cpu: [ppc64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.62.0':
resolution: {integrity: sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.62.0':
resolution: {integrity: sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.62.0':
resolution: {integrity: sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.62.0':
resolution: {integrity: sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.62.0':
resolution: {integrity: sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.62.0':
resolution: {integrity: sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==}
cpu: [x64]
os: [openbsd]
'@rollup/rollup-openharmony-arm64@4.62.0':
resolution: {integrity: sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-win32-arm64-msvc@4.62.0':
resolution: {integrity: sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.62.0':
resolution: {integrity: sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-gnu@4.62.0':
resolution: {integrity: sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.62.0':
resolution: {integrity: sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==}
cpu: [x64]
os: [win32]
'@types/estree@1.0.9':
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
'@vitejs/plugin-vue@5.2.4':
resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
engines: {node: ^18.0.0 || >=20.0.0}
peerDependencies:
vite: ^5.0.0 || ^6.0.0
vue: ^3.2.25
'@volar/language-core@2.4.15':
resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==}
'@volar/source-map@2.4.15':
resolution: {integrity: sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==}
'@volar/typescript@2.4.15':
resolution: {integrity: sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==}
'@vue/compiler-core@3.5.38':
resolution: {integrity: sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==}
'@vue/compiler-dom@3.5.38':
resolution: {integrity: sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==}
'@vue/compiler-sfc@3.5.38':
resolution: {integrity: sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==}
'@vue/compiler-ssr@3.5.38':
resolution: {integrity: sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==}
'@vue/compiler-vue2@2.7.16':
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
'@vue/language-core@2.2.12':
resolution: {integrity: sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
'@vue/reactivity@3.5.38':
resolution: {integrity: sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==}
'@vue/runtime-core@3.5.38':
resolution: {integrity: sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==}
'@vue/runtime-dom@3.5.38':
resolution: {integrity: sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==}
'@vue/server-renderer@3.5.38':
resolution: {integrity: sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==}
peerDependencies:
vue: 3.5.38
'@vue/shared@3.5.38':
resolution: {integrity: sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==}
alien-signals@1.0.13:
resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
brace-expansion@2.1.1:
resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==}
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
de-indent@1.0.2:
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
entities@7.0.1:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
esbuild@0.25.12:
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
engines: {node: '>=18'}
hasBin: true
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
he@1.2.0:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
minimatch@9.0.9:
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
engines: {node: '>=16 || 14 >=14.17'}
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
nanoid@3.3.12:
resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@4.0.4:
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
engines: {node: '>=12'}
postcss@8.5.15:
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
engines: {node: ^10 || ^12 || >=14}
rollup@4.62.0:
resolution: {integrity: sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
tinyglobby@0.2.17:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'}
typescript@5.7.3:
resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==}
engines: {node: '>=14.17'}
hasBin: true
vite@6.4.3:
resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
jiti: '>=1.21.0'
less: '*'
lightningcss: ^1.21.0
sass: '*'
sass-embedded: '*'
stylus: '*'
sugarss: '*'
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
peerDependenciesMeta:
'@types/node':
optional: true
jiti:
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
vscode-uri@3.1.0:
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
vue-tsc@2.2.12:
resolution: {integrity: sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==}
hasBin: true
peerDependencies:
typescript: '>=5.0.0'
vue@3.5.38:
resolution: {integrity: sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
snapshots:
'@babel/helper-string-parser@7.29.7': {}
'@babel/helper-validator-identifier@7.29.7': {}
'@babel/parser@7.29.7':
dependencies:
'@babel/types': 7.29.7
'@babel/types@7.29.7':
dependencies:
'@babel/helper-string-parser': 7.29.7
'@babel/helper-validator-identifier': 7.29.7
'@esbuild/aix-ppc64@0.25.12':
optional: true
'@esbuild/android-arm64@0.25.12':
optional: true
'@esbuild/android-arm@0.25.12':
optional: true
'@esbuild/android-x64@0.25.12':
optional: true
'@esbuild/darwin-arm64@0.25.12':
optional: true
'@esbuild/darwin-x64@0.25.12':
optional: true
'@esbuild/freebsd-arm64@0.25.12':
optional: true
'@esbuild/freebsd-x64@0.25.12':
optional: true
'@esbuild/linux-arm64@0.25.12':
optional: true
'@esbuild/linux-arm@0.25.12':
optional: true
'@esbuild/linux-ia32@0.25.12':
optional: true
'@esbuild/linux-loong64@0.25.12':
optional: true
'@esbuild/linux-mips64el@0.25.12':
optional: true
'@esbuild/linux-ppc64@0.25.12':
optional: true
'@esbuild/linux-riscv64@0.25.12':
optional: true
'@esbuild/linux-s390x@0.25.12':
optional: true
'@esbuild/linux-x64@0.25.12':
optional: true
'@esbuild/netbsd-arm64@0.25.12':
optional: true
'@esbuild/netbsd-x64@0.25.12':
optional: true
'@esbuild/openbsd-arm64@0.25.12':
optional: true
'@esbuild/openbsd-x64@0.25.12':
optional: true
'@esbuild/openharmony-arm64@0.25.12':
optional: true
'@esbuild/sunos-x64@0.25.12':
optional: true
'@esbuild/win32-arm64@0.25.12':
optional: true
'@esbuild/win32-ia32@0.25.12':
optional: true
'@esbuild/win32-x64@0.25.12':
optional: true
'@jridgewell/sourcemap-codec@1.5.5': {}
'@rollup/rollup-android-arm-eabi@4.62.0':
optional: true
'@rollup/rollup-android-arm64@4.62.0':
optional: true
'@rollup/rollup-darwin-arm64@4.62.0':
optional: true
'@rollup/rollup-darwin-x64@4.62.0':
optional: true
'@rollup/rollup-freebsd-arm64@4.62.0':
optional: true
'@rollup/rollup-freebsd-x64@4.62.0':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.62.0':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.62.0':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-arm64-musl@4.62.0':
optional: true
'@rollup/rollup-linux-loong64-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-loong64-musl@4.62.0':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-ppc64-musl@4.62.0':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.62.0':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-x64-gnu@4.62.0':
optional: true
'@rollup/rollup-linux-x64-musl@4.62.0':
optional: true
'@rollup/rollup-openbsd-x64@4.62.0':
optional: true
'@rollup/rollup-openharmony-arm64@4.62.0':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.62.0':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.62.0':
optional: true
'@rollup/rollup-win32-x64-gnu@4.62.0':
optional: true
'@rollup/rollup-win32-x64-msvc@4.62.0':
optional: true
'@types/estree@1.0.9': {}
'@vitejs/plugin-vue@5.2.4(vite@6.4.3)(vue@3.5.38(typescript@5.7.3))':
dependencies:
vite: 6.4.3
vue: 3.5.38(typescript@5.7.3)
'@volar/language-core@2.4.15':
dependencies:
'@volar/source-map': 2.4.15
'@volar/source-map@2.4.15': {}
'@volar/typescript@2.4.15':
dependencies:
'@volar/language-core': 2.4.15
path-browserify: 1.0.1
vscode-uri: 3.1.0
'@vue/compiler-core@3.5.38':
dependencies:
'@babel/parser': 7.29.7
'@vue/shared': 3.5.38
entities: 7.0.1
estree-walker: 2.0.2
source-map-js: 1.2.1
'@vue/compiler-dom@3.5.38':
dependencies:
'@vue/compiler-core': 3.5.38
'@vue/shared': 3.5.38
'@vue/compiler-sfc@3.5.38':
dependencies:
'@babel/parser': 7.29.7
'@vue/compiler-core': 3.5.38
'@vue/compiler-dom': 3.5.38
'@vue/compiler-ssr': 3.5.38
'@vue/shared': 3.5.38
estree-walker: 2.0.2
magic-string: 0.30.21
postcss: 8.5.15
source-map-js: 1.2.1
'@vue/compiler-ssr@3.5.38':
dependencies:
'@vue/compiler-dom': 3.5.38
'@vue/shared': 3.5.38
'@vue/compiler-vue2@2.7.16':
dependencies:
de-indent: 1.0.2
he: 1.2.0
'@vue/language-core@2.2.12(typescript@5.7.3)':
dependencies:
'@volar/language-core': 2.4.15
'@vue/compiler-dom': 3.5.38
'@vue/compiler-vue2': 2.7.16
'@vue/shared': 3.5.38
alien-signals: 1.0.13
minimatch: 9.0.9
muggle-string: 0.4.1
path-browserify: 1.0.1
optionalDependencies:
typescript: 5.7.3
'@vue/reactivity@3.5.38':
dependencies:
'@vue/shared': 3.5.38
'@vue/runtime-core@3.5.38':
dependencies:
'@vue/reactivity': 3.5.38
'@vue/shared': 3.5.38
'@vue/runtime-dom@3.5.38':
dependencies:
'@vue/reactivity': 3.5.38
'@vue/runtime-core': 3.5.38
'@vue/shared': 3.5.38
csstype: 3.2.3
'@vue/server-renderer@3.5.38(vue@3.5.38(typescript@5.7.3))':
dependencies:
'@vue/compiler-ssr': 3.5.38
'@vue/shared': 3.5.38
vue: 3.5.38(typescript@5.7.3)
'@vue/shared@3.5.38': {}
alien-signals@1.0.13: {}
balanced-match@1.0.2: {}
brace-expansion@2.1.1:
dependencies:
balanced-match: 1.0.2
csstype@3.2.3: {}
de-indent@1.0.2: {}
entities@7.0.1: {}
esbuild@0.25.12:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.12
'@esbuild/android-arm': 0.25.12
'@esbuild/android-arm64': 0.25.12
'@esbuild/android-x64': 0.25.12
'@esbuild/darwin-arm64': 0.25.12
'@esbuild/darwin-x64': 0.25.12
'@esbuild/freebsd-arm64': 0.25.12
'@esbuild/freebsd-x64': 0.25.12
'@esbuild/linux-arm': 0.25.12
'@esbuild/linux-arm64': 0.25.12
'@esbuild/linux-ia32': 0.25.12
'@esbuild/linux-loong64': 0.25.12
'@esbuild/linux-mips64el': 0.25.12
'@esbuild/linux-ppc64': 0.25.12
'@esbuild/linux-riscv64': 0.25.12
'@esbuild/linux-s390x': 0.25.12
'@esbuild/linux-x64': 0.25.12
'@esbuild/netbsd-arm64': 0.25.12
'@esbuild/netbsd-x64': 0.25.12
'@esbuild/openbsd-arm64': 0.25.12
'@esbuild/openbsd-x64': 0.25.12
'@esbuild/openharmony-arm64': 0.25.12
'@esbuild/sunos-x64': 0.25.12
'@esbuild/win32-arm64': 0.25.12
'@esbuild/win32-ia32': 0.25.12
'@esbuild/win32-x64': 0.25.12
estree-walker@2.0.2: {}
fdir@6.5.0(picomatch@4.0.4):
optionalDependencies:
picomatch: 4.0.4
fsevents@2.3.3:
optional: true
he@1.2.0: {}
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
minimatch@9.0.9:
dependencies:
brace-expansion: 2.1.1
muggle-string@0.4.1: {}
nanoid@3.3.12: {}
path-browserify@1.0.1: {}
picocolors@1.1.1: {}
picomatch@4.0.4: {}
postcss@8.5.15:
dependencies:
nanoid: 3.3.12
picocolors: 1.1.1
source-map-js: 1.2.1
rollup@4.62.0:
dependencies:
'@types/estree': 1.0.9
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.62.0
'@rollup/rollup-android-arm64': 4.62.0
'@rollup/rollup-darwin-arm64': 4.62.0
'@rollup/rollup-darwin-x64': 4.62.0
'@rollup/rollup-freebsd-arm64': 4.62.0
'@rollup/rollup-freebsd-x64': 4.62.0
'@rollup/rollup-linux-arm-gnueabihf': 4.62.0
'@rollup/rollup-linux-arm-musleabihf': 4.62.0
'@rollup/rollup-linux-arm64-gnu': 4.62.0
'@rollup/rollup-linux-arm64-musl': 4.62.0
'@rollup/rollup-linux-loong64-gnu': 4.62.0
'@rollup/rollup-linux-loong64-musl': 4.62.0
'@rollup/rollup-linux-ppc64-gnu': 4.62.0
'@rollup/rollup-linux-ppc64-musl': 4.62.0
'@rollup/rollup-linux-riscv64-gnu': 4.62.0
'@rollup/rollup-linux-riscv64-musl': 4.62.0
'@rollup/rollup-linux-s390x-gnu': 4.62.0
'@rollup/rollup-linux-x64-gnu': 4.62.0
'@rollup/rollup-linux-x64-musl': 4.62.0
'@rollup/rollup-openbsd-x64': 4.62.0
'@rollup/rollup-openharmony-arm64': 4.62.0
'@rollup/rollup-win32-arm64-msvc': 4.62.0
'@rollup/rollup-win32-ia32-msvc': 4.62.0
'@rollup/rollup-win32-x64-gnu': 4.62.0
'@rollup/rollup-win32-x64-msvc': 4.62.0
fsevents: 2.3.3
source-map-js@1.2.1: {}
tinyglobby@0.2.17:
dependencies:
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
typescript@5.7.3: {}
vite@6.4.3:
dependencies:
esbuild: 0.25.12
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
postcss: 8.5.15
rollup: 4.62.0
tinyglobby: 0.2.17
optionalDependencies:
fsevents: 2.3.3
vscode-uri@3.1.0: {}
vue-tsc@2.2.12(typescript@5.7.3):
dependencies:
'@volar/typescript': 2.4.15
'@vue/language-core': 2.2.12(typescript@5.7.3)
typescript: 5.7.3
vue@3.5.38(typescript@5.7.3):
dependencies:
'@vue/compiler-dom': 3.5.38
'@vue/compiler-sfc': 3.5.38
'@vue/runtime-dom': 3.5.38
'@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@5.7.3))
'@vue/shared': 3.5.38
optionalDependencies:
typescript: 5.7.3
-2
View File
@@ -1,2 +0,0 @@
allowBuilds:
esbuild: true
-137
View File
@@ -1,137 +0,0 @@
<template>
<GalaxyCanvas />
<PreloaderView :visible="showPreloader" />
<div :class="['page-shell', { 'page-shell--ready': !showPreloader }]">
<main class="container">
<HeroSection :active="!showPreloader" />
</main>
<FooterView />
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
import GalaxyCanvas from './components/GalaxyCanvas.vue'
import HeroSection from './components/HeroSection.vue'
import FooterView from './components/FooterView.vue'
import PreloaderView from './components/PreloaderView.vue'
const showPreloader = ref(true)
let preloaderTimer = 0
onMounted(() => {
const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
preloaderTimer = window.setTimeout(() => {
showPreloader.value = false
}, reducedMotion ? 500 : 3200)
})
onUnmounted(() => {
window.clearTimeout(preloaderTimer)
})
</script>
<style>
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
color-scheme: dark;
--bg: #02030a;
--surface: rgba(16, 10, 35, 0.58);
--border: rgba(198, 174, 255, 0.2);
--border-bright: rgba(226, 208, 255, 0.38);
--text: #fbf7ff;
--text-muted: #c8b8dd;
--text-dim: #786989;
--accent: #a887ff;
--violet: #5d37b8;
--lilac: #dec9ff;
--deep-blue: #070d22;
}
html {
min-height: 100%;
}
body {
font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', Arial, sans-serif;
background:
radial-gradient(circle at 50% 36%, rgba(93, 55, 184, 0.28), transparent 31rem),
radial-gradient(circle at 76% 18%, rgba(222, 201, 255, 0.11), transparent 25rem),
radial-gradient(circle at 18% 78%, rgba(7, 13, 34, 0.92), transparent 24rem),
linear-gradient(180deg, #02030a 0%, #0a0618 45%, #070d22 74%, #010207 100%);
color: var(--text);
min-height: 100vh;
overflow: hidden;
}
body::before,
body::after {
content: '';
position: fixed;
inset: 0;
pointer-events: none;
z-index: 1;
}
body::before {
background:
linear-gradient(90deg, rgba(226, 208, 255, 0.03) 1px, transparent 1px),
linear-gradient(180deg, rgba(143, 122, 214, 0.026) 1px, transparent 1px);
background-size: 96px 96px;
mask-image: radial-gradient(circle at 50% 46%, black, transparent 68%);
opacity: 0.42;
}
body::after {
background:
radial-gradient(circle at 50% 48%, transparent 0 30%, rgba(1, 2, 7, 0.72) 100%),
repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.022) 0 1px, transparent 1px 4px);
mix-blend-mode: soft-light;
opacity: 0.72;
}
#app {
min-height: 100vh;
position: relative;
}
.page-shell {
min-height: 100vh;
display: grid;
grid-template-rows: 1fr auto;
opacity: 0;
transition: opacity 900ms ease;
}
.page-shell--ready {
opacity: 1;
}
.container {
width: min(1180px, calc(100vw - 2rem));
margin: 0 auto;
position: relative;
z-index: 2;
display: grid;
place-items: center;
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
</style>
-51
View File
@@ -1,51 +0,0 @@
<template>
<footer>
<p>Minh Bao - 2026</p>
</footer>
</template>
<style scoped>
footer {
position: relative;
z-index: 2;
text-align: center;
padding: 0 1rem 1.7rem;
}
footer p {
color: #a994bd;
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
text-shadow: 0 0 24px rgba(222, 201, 255, 0.22);
opacity: 0;
transform: translateY(16px);
animation: footer-reveal 1.2s ease 8.45s forwards;
}
@keyframes footer-reveal {
from {
opacity: 0;
transform: translateY(16px);
filter: blur(6px);
}
to {
opacity: 1;
transform: translateY(0);
filter: blur(0);
}
}
@media (max-width: 760px) {
footer {
padding-bottom: 1.45rem;
}
footer p {
color: #b8a6c8;
font-size: 0.74rem;
}
}
</style>
-271
View File
@@ -1,271 +0,0 @@
<template>
<canvas ref="canvasRef" class="galaxy-canvas"></canvas>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
const canvasRef = ref<HTMLCanvasElement | null>(null)
let animationId = 0
let cleanup: (() => void) | undefined
interface Star {
x: number
y: number
z: number
size: number
opacity: number
speed: number
tint: string
}
interface DriftParticle {
x: number
y: number
radius: number
opacity: number
speed: number
phase: number
}
interface Comet {
x: number
y: number
length: number
speed: number
opacity: number
delay: number
}
onMounted(() => {
const canvas = canvasRef.value
if (!canvas) return
const ctx = canvas.getContext('2d')
if (!ctx) return
const stars: Star[] = []
const dust: DriftParticle[] = []
const comets: Comet[] = []
const STAR_COUNT = 220
const DUST_COUNT = 42
const COMET_COUNT = 5
const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
const mouse = { x: 0, y: 0 }
const viewport = { width: 0, height: 0, ratio: 1 }
function resize() {
viewport.width = window.innerWidth
viewport.height = window.innerHeight
viewport.ratio = Math.min(window.devicePixelRatio || 1, 2)
canvas!.width = Math.floor(viewport.width * viewport.ratio)
canvas!.height = Math.floor(viewport.height * viewport.ratio)
canvas!.style.width = `${viewport.width}px`
canvas!.style.height = `${viewport.height}px`
ctx!.setTransform(viewport.ratio, 0, 0, viewport.ratio, 0, 0)
seedScene()
}
function seedScene() {
stars.length = 0
dust.length = 0
comets.length = 0
for (let i = 0; i < STAR_COUNT; i += 1) {
const cool = Math.random() > 0.18
stars.push({
x: Math.random() * viewport.width - viewport.width / 2,
y: Math.random() * viewport.height - viewport.height / 2,
z: Math.random() * 1000,
size: Math.random() * 1.8 + 0.35,
opacity: Math.random() * 0.75 + 0.18,
speed: reducedMotion ? 0 : Math.random() * 0.22 + 0.035,
tint: cool ? '225, 207, 255' : '170, 142, 255',
})
}
for (let i = 0; i < DUST_COUNT; i += 1) {
dust.push({
x: Math.random() * viewport.width,
y: Math.random() * viewport.height,
radius: Math.random() * 260 + 80,
opacity: Math.random() * 0.055 + 0.018,
speed: reducedMotion ? 0 : Math.random() * 0.08 + 0.015,
phase: Math.random() * Math.PI * 2,
})
}
for (let i = 0; i < COMET_COUNT; i += 1) {
comets.push({
x: Math.random() * viewport.width,
y: Math.random() * viewport.height * 0.75,
length: Math.random() * 160 + 110,
speed: reducedMotion ? 0 : Math.random() * 2 + 1.2,
opacity: Math.random() * 0.24 + 0.14,
delay: Math.random() * 900,
})
}
}
function onMouseMove(e: MouseEvent) {
mouse.x = (e.clientX / window.innerWidth - 0.5) * 2
mouse.y = (e.clientY / window.innerHeight - 0.5) * 2
}
function drawNebula(time: number) {
const pulse = reducedMotion ? 0 : Math.sin(time * 0.00022) * 22
const cx = viewport.width * 0.5 + mouse.x * 42
const cy = viewport.height * 0.46 + mouse.y * 28
const drift = reducedMotion ? 0 : Math.sin(time * 0.00012) * viewport.width * 0.025
const core = ctx!.createRadialGradient(cx, cy, 0, cx, cy, viewport.width * 0.62)
core.addColorStop(0, 'rgba(222, 201, 255, 0.2)')
core.addColorStop(0.22, 'rgba(93, 55, 184, 0.14)')
core.addColorStop(0.52, 'rgba(24, 16, 58, 0.1)')
core.addColorStop(1, 'rgba(2, 3, 10, 0)')
ctx!.fillStyle = core
ctx!.fillRect(0, 0, viewport.width, viewport.height)
const veil = ctx!.createRadialGradient(
viewport.width * 0.72,
viewport.height * 0.18 + pulse,
0,
viewport.width * 0.72,
viewport.height * 0.18,
viewport.width * 0.48,
)
veil.addColorStop(0, 'rgba(184, 156, 255, 0.08)')
veil.addColorStop(0.45, 'rgba(30, 45, 101, 0.09)')
veil.addColorStop(1, 'rgba(3, 5, 16, 0)')
ctx!.fillStyle = veil
ctx!.fillRect(0, 0, viewport.width, viewport.height)
const rift = ctx!.createLinearGradient(
viewport.width * 0.12 + drift,
viewport.height * 0.12,
viewport.width * 0.86 + drift,
viewport.height * 0.86,
)
rift.addColorStop(0, 'rgba(2, 3, 10, 0)')
rift.addColorStop(0.28, 'rgba(7, 13, 34, 0.22)')
rift.addColorStop(0.5, 'rgba(222, 201, 255, 0.055)')
rift.addColorStop(0.72, 'rgba(7, 13, 34, 0.18)')
rift.addColorStop(1, 'rgba(2, 3, 10, 0)')
ctx!.save()
ctx!.globalCompositeOperation = 'screen'
ctx!.fillStyle = rift
ctx!.fillRect(0, 0, viewport.width, viewport.height)
ctx!.restore()
}
function drawDust(time: number) {
ctx!.globalCompositeOperation = 'lighter'
for (const particle of dust) {
particle.phase += particle.speed * 0.004
const x = particle.x + Math.sin(time * 0.00008 + particle.phase) * 34 + mouse.x * 10
const y = particle.y + Math.cos(time * 0.00006 + particle.phase) * 22 + mouse.y * 8
const gradient = ctx!.createRadialGradient(x, y, 0, x, y, particle.radius)
gradient.addColorStop(0, `rgba(222, 201, 255, ${particle.opacity * 1.12})`)
gradient.addColorStop(0.55, `rgba(83, 102, 184, ${particle.opacity * 0.34})`)
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)')
ctx!.fillStyle = gradient
ctx!.fillRect(x - particle.radius, y - particle.radius, particle.radius * 2, particle.radius * 2)
}
ctx!.globalCompositeOperation = 'source-over'
}
function drawComets() {
for (const comet of comets) {
comet.delay -= 1
if (comet.delay > 0) continue
comet.x += comet.speed * 1.9
comet.y += comet.speed * 0.78
if (comet.x > viewport.width + comet.length || comet.y > viewport.height + 80) {
comet.x = Math.random() * viewport.width * 0.6 - viewport.width * 0.22
comet.y = Math.random() * viewport.height * 0.35
comet.delay = Math.random() * 1000 + 420
}
const tail = ctx!.createLinearGradient(comet.x, comet.y, comet.x - comet.length, comet.y - comet.length * 0.38)
tail.addColorStop(0, `rgba(250, 244, 255, ${comet.opacity})`)
tail.addColorStop(0.16, `rgba(205, 184, 255, ${comet.opacity * 0.5})`)
tail.addColorStop(1, 'rgba(111, 125, 210, 0)')
ctx!.strokeStyle = tail
ctx!.lineWidth = 1
ctx!.beginPath()
ctx!.moveTo(comet.x, comet.y)
ctx!.lineTo(comet.x - comet.length, comet.y - comet.length * 0.38)
ctx!.stroke()
}
}
function animate() {
const time = performance.now()
ctx!.fillStyle = 'rgba(2, 3, 10, 0.32)'
ctx!.fillRect(0, 0, viewport.width, viewport.height)
drawNebula(time)
drawDust(time)
const cx = viewport.width / 2 + mouse.x * 48
const cy = viewport.height / 2 + mouse.y * 34
for (const star of stars) {
star.z -= star.speed * 2
if (star.z <= 0) {
star.z = 1000
star.x = Math.random() * viewport.width - viewport.width / 2
star.y = Math.random() * viewport.height - viewport.height / 2
star.opacity = Math.random() * 0.8 + 0.2
}
const scale = 800 / star.z
const sx = star.x * scale + cx
const sy = star.y * scale + cy
const size = star.size * scale * 0.5
if (sx < 0 || sx > viewport.width || sy < 0 || sy > viewport.height) continue
const alpha = Math.max(0, Math.min(1, (1 - star.z / 1000) * star.opacity))
ctx!.beginPath()
ctx!.arc(sx, sy, Math.max(0.5, size), 0, Math.PI * 2)
ctx!.fillStyle = `rgba(${star.tint}, ${alpha})`
ctx!.fill()
}
drawComets()
animationId = requestAnimationFrame(animate)
}
resize()
ctx.fillStyle = '#02030a'
ctx.fillRect(0, 0, viewport.width, viewport.height)
setTimeout(animate, 100)
window.addEventListener('resize', resize)
window.addEventListener('mousemove', onMouseMove)
cleanup = () => {
cancelAnimationFrame(animationId)
window.removeEventListener('resize', resize)
window.removeEventListener('mousemove', onMouseMove)
}
})
onUnmounted(() => cleanup?.())
</script>
<style scoped>
.galaxy-canvas {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none;
}
</style>
-349
View File
@@ -1,349 +0,0 @@
<template>
<section class="hero" :class="{ 'hero--active': active }" aria-labelledby="hero-title">
<div class="galaxy-aura" aria-hidden="true">
<span class="aura-cloud"></span>
<span class="aperture-arc arc-one"></span>
<span class="aperture-arc arc-two"></span>
<span class="aperture-arc arc-three"></span>
<span class="dust-ribbon"></span>
<span class="star-stream"></span>
</div>
<div class="cinema-mark" aria-hidden="true"></div>
<h1 id="hero-title" aria-label="NOVERIA OPERATIONS">
<span
v-for="word in titleWords"
:key="word.text"
class="title-word"
aria-hidden="true"
>
<span
v-for="letter in word.letters"
:key="`${word.text}-${letter.index}`"
class="title-letter"
:style="{ '--delay': `${letter.index * 0.11}s` }"
>
{{ letter.char }}
</span>
</span>
</h1>
<p class="coming-soon">COMING SOON</p>
</section>
</template>
<script setup lang="ts">
defineProps<{
active: boolean
}>()
const titleWords = ['NOVERIA', 'OPERATIONS'].map((text, wordIndex) => {
const offset = wordIndex === 0 ? 0 : 'NOVERIA'.length + 1
return {
text,
letters: Array.from(text).map((char, index) => ({
char,
index: offset + index,
})),
}
})
</script>
<style scoped>
.hero {
width: 100%;
min-height: calc(100vh - 5rem);
display: grid;
place-items: center;
align-content: center;
text-align: center;
position: relative;
isolation: isolate;
}
.hero::before {
content: '';
position: absolute;
left: 50%;
top: 50%;
border-radius: 999px;
translate: -50% -50%;
z-index: -1;
}
.hero::before {
width: min(880px, 88vw);
height: min(880px, 88vw);
background:
radial-gradient(circle, rgba(205, 176, 255, 0.18), transparent 30%),
radial-gradient(circle, rgba(89, 55, 176, 0.14), transparent 57%);
filter: blur(28px);
}
.galaxy-aura {
position: absolute;
left: 50%;
top: 50%;
width: min(1060px, 96vw);
height: min(560px, 64vw);
translate: -50% -34%;
z-index: -1;
pointer-events: none;
opacity: 0;
}
.hero--active .galaxy-aura {
animation: aura-reveal 2.8s ease 0.1s forwards;
}
.aura-cloud,
.aperture-arc,
.dust-ribbon,
.star-stream {
position: absolute;
inset: 0;
display: block;
}
.aura-cloud {
border-radius: 50%;
background:
radial-gradient(circle at 48% 48%, rgba(230, 214, 255, 0.08), transparent 20%),
radial-gradient(circle at 36% 58%, rgba(171, 128, 255, 0.055), transparent 34%),
radial-gradient(circle at 68% 34%, rgba(55, 73, 148, 0.08), transparent 44%);
filter: blur(34px);
opacity: 0.34;
transform: rotate(-7deg) scaleX(1.08);
}
.aperture-arc {
border: 1px solid rgba(224, 202, 255, 0.24);
border-radius: 50%;
background: transparent;
filter: drop-shadow(0 0 18px rgba(168, 135, 255, 0.22));
mask-image: conic-gradient(
from 210deg,
transparent 0deg 32deg,
black 46deg 92deg,
transparent 112deg 218deg,
black 236deg 256deg,
transparent 278deg 360deg
);
}
.arc-one {
inset: 9% 3%;
transform: rotate(-12deg);
}
.arc-two {
inset: 19% 13%;
transform: rotate(10deg);
opacity: 0.72;
border-color: rgba(179, 150, 255, 0.18);
mask-image: conic-gradient(
from 24deg,
black 0deg 34deg,
transparent 58deg 198deg,
black 218deg 254deg,
transparent 278deg 360deg
);
}
.arc-three {
inset: 31% 23%;
transform: rotate(-31deg);
opacity: 0.42;
border-color: rgba(226, 208, 255, 0.2);
mask-image: conic-gradient(from 148deg, transparent 0deg 38deg, black 54deg 78deg, transparent 96deg 360deg);
}
.dust-ribbon {
inset: 39% 2% auto;
height: 18%;
border-radius: 50%;
background:
radial-gradient(circle at 24% 62%, rgba(225, 207, 255, 0.2), transparent 1.6px),
radial-gradient(circle at 38% 44%, rgba(225, 207, 255, 0.14), transparent 1px),
radial-gradient(circle at 56% 52%, rgba(143, 122, 214, 0.18), transparent 1.8px),
radial-gradient(circle at 72% 38%, rgba(225, 207, 255, 0.18), transparent 1px),
linear-gradient(104deg, transparent 22%, rgba(222, 201, 255, 0.035) 45%, transparent 75%);
filter: blur(2.5px);
opacity: 0.2;
transform: rotate(-9deg);
}
.star-stream {
inset: 42% -3% auto;
height: 2px;
background:
radial-gradient(circle, rgba(245, 236, 255, 0.82) 0 1px, transparent 2px) 0 0 / 96px 2px,
linear-gradient(90deg, transparent, rgba(225, 202, 255, 0.32), rgba(79, 97, 178, 0.18), transparent);
filter: blur(0.2px);
opacity: 0.72;
transform: rotate(-13deg);
}
.cinema-mark {
width: 1px;
height: 88px;
margin-bottom: 2.4rem;
background: linear-gradient(180deg, transparent, rgba(229, 211, 255, 0.82), transparent);
box-shadow: 0 0 42px rgba(165, 126, 255, 0.48);
opacity: 0;
}
.hero--active .cinema-mark {
animation: mark-reveal 1.45s ease 0.15s forwards;
}
h1 {
display: flex;
justify-content: center;
align-items: baseline;
flex-wrap: wrap;
gap: 0 0.28em;
max-width: 1120px;
font-size: clamp(3rem, 10.8vw, 9.7rem);
line-height: 0.9;
font-weight: 900;
letter-spacing: 0;
text-transform: uppercase;
perspective: 900px;
}
.title-word {
display: inline-flex;
white-space: nowrap;
}
.title-letter {
display: inline-block;
min-width: 0.52em;
background: linear-gradient(180deg, #fffaff 0%, #e2ccff 43%, #9b73ff 76%, #5140b9 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
opacity: 0;
filter: blur(18px) brightness(1.18);
transform: scale(1.075) rotateX(18deg);
transform-origin: 50% 58%;
text-shadow:
0 0 50px rgba(224, 202, 255, 0.42),
0 0 120px rgba(109, 83, 214, 0.34);
will-change: opacity, filter, transform;
}
.hero--active .title-letter {
animation: gravitational-reveal 1.9s cubic-bezier(0.16, 1, 0.3, 1) calc(0.42s + var(--delay)) forwards;
}
.coming-soon {
margin-top: 2.2rem;
color: #e1ccff;
font-size: clamp(0.92rem, 2vw, 1.22rem);
font-weight: 800;
letter-spacing: 0.36em;
text-transform: uppercase;
opacity: 0;
text-shadow: 0 0 32px rgba(166, 126, 255, 0.5);
}
.hero--active .coming-soon {
animation: subtitle-reveal 1.4s ease 2.95s forwards;
}
@keyframes aura-reveal {
0% {
opacity: 0;
}
68% {
opacity: 0.72;
}
100% {
opacity: 1;
}
}
@keyframes gravitational-reveal {
0% {
opacity: 0;
filter: blur(18px) brightness(1.18);
transform: scale(1.075) rotateX(18deg);
}
52% {
opacity: 1;
filter: blur(3px) brightness(1.08);
transform: scale(1.018) rotateX(4deg);
}
100% {
opacity: 1;
filter: blur(0) brightness(1);
transform: scale(1) rotateX(0deg);
}
}
@keyframes subtitle-reveal {
from {
opacity: 0;
transform: translateY(14px);
filter: blur(8px);
}
to {
opacity: 0.86;
transform: translateY(0);
filter: blur(0);
}
}
@keyframes mark-reveal {
from {
opacity: 0;
transform: scaleY(0.25);
}
to {
opacity: 0.72;
transform: scaleY(1);
}
}
@media (max-width: 760px) {
.hero {
min-height: calc(100vh - 5rem);
}
h1 {
font-size: clamp(2.25rem, 11.8vw, 3.35rem);
max-width: min(360px, 100%);
gap: 0.04em 0.2em;
}
.title-letter {
min-width: 0.48em;
}
.galaxy-aura {
width: min(680px, 118vw);
height: min(420px, 84vw);
translate: -50% -37%;
}
.aura-cloud {
opacity: 0.72;
filter: blur(20px);
}
.aperture-arc {
opacity: 0.58;
}
.coming-soon {
letter-spacing: 0.24em;
}
}
</style>
-213
View File
@@ -1,213 +0,0 @@
<template>
<Transition name="preloader-fade">
<div v-if="visible" class="preloader" aria-hidden="true">
<div class="preloader-core">
<div class="preloader-aura">
<span class="pre-orbit pre-orbit-one"></span>
<span class="pre-orbit pre-orbit-two"></span>
<span class="pre-dust"></span>
<span class="pre-flare"></span>
</div>
<div class="pre-copy">
<p class="pre-status" aria-label="Initializing the dark orbit">
<span
v-for="(letter, index) in statusLetters"
:key="`${letter}-${index}`"
class="pre-status-letter"
:class="{ 'is-space': letter === ' ' }"
:style="{ '--delay': `${index * 0.04}s` }"
>{{ letter === ' ' ? ' ' : letter }}</span>
</p>
</div>
</div>
</div>
</Transition>
</template>
<script setup lang="ts">
defineProps<{
visible: boolean
}>()
const statusLetters = Array.from('Initializing the dark orbit')
</script>
<style scoped>
.preloader {
position: fixed;
inset: 0;
z-index: 20;
display: grid;
place-items: center;
background:
radial-gradient(circle at 50% 42%, rgba(222, 201, 255, 0.06), transparent 22rem),
radial-gradient(circle at 68% 26%, rgba(93, 55, 184, 0.12), transparent 24rem),
linear-gradient(180deg, rgba(2, 3, 10, 0.42), rgba(3, 5, 18, 0.52));
backdrop-filter: blur(2px);
}
.preloader-core {
width: min(560px, calc(100vw - 2rem));
display: grid;
justify-items: center;
gap: 0.65rem;
}
.preloader-aura {
position: relative;
width: min(260px, 54vw);
aspect-ratio: 1;
}
.preloader-aura::before {
content: '';
position: absolute;
inset: 14%;
border-radius: 50%;
background:
radial-gradient(circle at 50% 50%, rgba(225, 207, 255, 0.2), transparent 32%),
radial-gradient(circle at 52% 48%, rgba(70, 82, 158, 0.16), transparent 62%);
filter: blur(20px);
animation: preload-pulse 3.2s ease-in-out infinite;
}
.pre-orbit,
.pre-dust,
.pre-flare {
position: absolute;
inset: 0;
display: block;
}
.pre-orbit {
border-radius: 50%;
border: 2px solid rgba(226, 208, 255, 0.34);
filter: drop-shadow(0 0 16px rgba(168, 135, 255, 0.26));
}
.pre-orbit-one {
inset: 8%;
transform: rotate(-18deg);
mask-image: conic-gradient(from 214deg, transparent 0deg 38deg, black 56deg 130deg, transparent 164deg 236deg, black 258deg 306deg, transparent 336deg);
animation: preload-rotate 5.5s linear infinite;
}
.pre-orbit-two {
inset: 21%;
border-color: rgba(165, 144, 236, 0.3);
transform: rotate(11deg);
mask-image: conic-gradient(from 28deg, black 0deg 42deg, transparent 62deg 210deg, black 228deg 260deg, transparent 286deg);
animation: preload-rotate-reverse 7.4s linear infinite;
}
.pre-dust {
inset: 33% 10%;
height: 34%;
border-radius: 50%;
background:
radial-gradient(circle at 16% 58%, rgba(225, 207, 255, 0.26), transparent 1.8px),
radial-gradient(circle at 42% 46%, rgba(225, 207, 255, 0.16), transparent 1.1px),
radial-gradient(circle at 76% 40%, rgba(143, 122, 214, 0.22), transparent 1.8px),
linear-gradient(104deg, transparent 18%, rgba(222, 201, 255, 0.055) 46%, transparent 78%);
filter: blur(2.4px);
opacity: 0.42;
transform: rotate(-10deg);
}
.pre-flare {
inset: 48% -12% auto;
height: 2px;
background:
radial-gradient(circle, rgba(245, 236, 255, 0.8) 0 1px, transparent 2px) 0 0 / 74px 2px,
linear-gradient(90deg, transparent, rgba(225, 202, 255, 0.32), rgba(79, 97, 178, 0.18), transparent);
opacity: 0.72;
transform: rotate(-12deg);
}
.pre-copy {
display: grid;
justify-items: center;
gap: 0.8rem;
}
.pre-status {
display: flex;
flex-wrap: wrap;
justify-content: center;
color: #b7a5cd;
text-transform: uppercase;
letter-spacing: 0.26em;
font-size: 0.72rem;
}
.pre-status-letter {
display: inline-block;
opacity: 0;
filter: blur(10px);
transform: translateY(0.3em);
animation: pre-letter-reveal 0.9s cubic-bezier(0.16, 1, 0.3, 1) calc(0.18s + var(--delay)) forwards;
}
.pre-status-letter.is-space {
width: 0.4em;
}
.preloader-fade-enter-active,
.preloader-fade-leave-active {
transition: opacity 800ms ease;
}
.preloader-fade-enter-from,
.preloader-fade-leave-to {
opacity: 0;
}
@keyframes preload-pulse {
0%, 100% {
opacity: 0.68;
transform: scale(0.98);
}
50% {
opacity: 1;
transform: scale(1.02);
}
}
@keyframes preload-rotate {
to {
transform: rotate(342deg);
}
}
@keyframes preload-rotate-reverse {
to {
transform: rotate(-349deg);
}
}
@keyframes pre-letter-reveal {
0% {
opacity: 0;
filter: blur(10px);
transform: translateY(0.3em);
}
100% {
opacity: 1;
filter: blur(0);
transform: translateY(0);
}
}
@media (max-width: 760px) {
.preloader-core {
gap: 0.5rem;
}
.pre-status {
font-size: 0.66rem;
letter-spacing: 0.18em;
}
}
</style>
-290
View File
@@ -1,290 +0,0 @@
<template>
<section class="section-block">
<div class="section-header">
<span class="signal"></span>
<h2>{{ title }}</h2>
<span class="line"></span>
</div>
<div v-if="layout === 'cards'" class="cards">
<article v-for="item in items" :key="item.title" class="card">
<div class="icon">{{ item.icon }}</div>
<h3>{{ item.title }}</h3>
<p>{{ item.description }}</p>
<p v-if="item.meta" class="meta">{{ item.meta }}</p>
</article>
</div>
<div v-else class="stack">
<article v-for="item in items" :key="item.title" class="stack-item">
<div class="left">
<span class="emoji">{{ item.icon }}</span>
<div class="info">
<h4>{{ item.title }}</h4>
<span>{{ item.description }}</span>
</div>
</div>
<span v-if="item.badge" :class="['badge', item.badgeClass || '']">{{ item.badge }}</span>
</article>
</div>
</section>
</template>
<script setup lang="ts">
defineProps<{
title: string
layout?: 'cards' | 'stack'
items: Array<{
icon: string
title: string
description: string
meta?: string
badge?: string
badgeClass?: string
}>
}>()
</script>
<style scoped>
.section-block {
position: relative;
padding: 1.2rem 0 2.8rem;
animation: reveal 720ms ease both;
animation-timeline: view();
animation-range: entry 0% cover 28%;
}
.section-header {
display: flex;
align-items: center;
gap: 0.9rem;
margin-bottom: 1.25rem;
padding-top: 1rem;
}
.section-header .signal {
width: 0.58rem;
height: 0.58rem;
border-radius: 50%;
background: var(--cyan);
box-shadow:
0 0 18px rgba(126, 216, 255, 0.55),
0 0 42px rgba(157, 131, 255, 0.18);
}
.section-header h2 {
font-size: clamp(1.05rem, 1.8vw, 1.38rem);
font-weight: 700;
color: var(--text);
letter-spacing: 0.04em;
text-transform: uppercase;
}
.section-header .line {
flex: 1;
height: 1px;
background: linear-gradient(90deg, var(--border-bright), transparent);
}
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.card {
min-height: 210px;
background:
linear-gradient(145deg, rgba(255, 255, 255, 0.045), transparent 32%),
var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 1.5rem;
box-shadow:
0 26px 70px rgba(0, 0, 0, 0.24),
inset 0 1px 0 rgba(255, 255, 255, 0.05);
backdrop-filter: blur(18px);
transition: transform 220ms ease, border-color 220ms ease, background 220ms ease;
cursor: default;
position: relative;
overflow: hidden;
}
.card::after {
content: '';
position: absolute;
inset: auto 1rem 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(126, 216, 255, 0.42), transparent);
opacity: 0;
transition: opacity 220ms ease;
}
.card:hover {
background:
linear-gradient(145deg, rgba(126, 216, 255, 0.08), transparent 36%),
var(--surface-strong);
border-color: rgba(126, 216, 255, 0.36);
transform: translateY(-4px);
}
.card:hover::after {
opacity: 1;
}
.card .icon {
width: 2.6rem;
height: 2.6rem;
display: grid;
place-items: center;
border: 1px solid rgba(126, 216, 255, 0.2);
border-radius: 8px;
background: rgba(126, 216, 255, 0.055);
color: var(--cyan);
font-size: 0.8rem;
font-weight: 800;
letter-spacing: 0.12em;
margin-bottom: 1.1rem;
}
.card h3 {
font-size: 1.05rem;
font-weight: 700;
margin-bottom: 0.55rem;
color: var(--text);
}
.card p {
font-size: 0.92rem;
color: var(--text-muted);
line-height: 1.65;
}
.card .meta {
font-size: 0.8rem;
margin-top: 1rem;
color: var(--text-dim);
}
.stack {
display: flex;
flex-direction: column;
gap: 1rem;
margin-bottom: 2rem;
}
.stack-item {
min-height: 88px;
background:
linear-gradient(90deg, rgba(126, 216, 255, 0.06), transparent 34%),
var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 1.25rem 1.5rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
box-shadow: 0 22px 54px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(18px);
transition: transform 220ms ease, border-color 220ms ease, background 220ms ease;
}
.stack-item:hover {
background:
linear-gradient(90deg, rgba(157, 131, 255, 0.12), transparent 42%),
var(--surface-strong);
border-color: rgba(157, 131, 255, 0.36);
transform: translateX(5px);
}
.stack-item .left {
display: flex;
align-items: center;
gap: 0.85rem;
}
.stack-item .left .emoji {
flex: 0 0 auto;
width: 2.35rem;
height: 2.35rem;
display: grid;
place-items: center;
border: 1px solid rgba(157, 131, 255, 0.22);
border-radius: 8px;
color: #c7bbff;
background: rgba(157, 131, 255, 0.075);
font-size: 0.72rem;
font-weight: 800;
letter-spacing: 0.08em;
}
.stack-item .left .info h4 {
font-size: 1rem;
font-weight: 700;
color: var(--text);
}
.stack-item .left .info span {
font-size: 0.86rem;
color: var(--text-muted);
}
.badge {
background: rgba(157, 131, 255, 0.12);
color: #c7bbff;
border: 1px solid rgba(157, 131, 255, 0.2);
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.78rem;
font-weight: 500;
white-space: nowrap;
}
.badge.green {
background: rgba(53, 231, 179, 0.1);
color: var(--green);
border-color: rgba(53, 231, 179, 0.2);
}
.badge.yellow {
background: rgba(255, 209, 102, 0.1);
color: var(--amber);
border-color: rgba(255, 209, 102, 0.2);
}
@keyframes reveal {
from {
opacity: 0;
transform: translateY(28px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 640px) {
.cards { grid-template-columns: 1fr; }
.stack-item {
flex-direction: column;
align-items: flex-start;
padding: 1.15rem;
}
.section-block {
padding-bottom: 2rem;
}
}
@media (prefers-reduced-motion: reduce) {
.section-block {
animation: none;
}
.card:hover,
.stack-item:hover {
transform: none;
}
}
</style>
-7
View File
@@ -1,7 +0,0 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
-4
View File
@@ -1,4 +0,0 @@
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
-23
View File
@@ -1,23 +0,0 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}
-1
View File
@@ -1 +0,0 @@
{"root":["./src/env.d.ts","./src/main.ts","./src/app.vue","./src/components/footerview.vue","./src/components/galaxycanvas.vue","./src/components/herosection.vue","./src/components/preloaderview.vue","./src/components/sectionblock.vue"],"version":"5.7.3"}
-7
View File
@@ -1,7 +0,0 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
-19
View File
@@ -1,19 +0,0 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2023"],
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}
-1
View File
@@ -1 +0,0 @@
{"root":["./vite.config.ts"],"version":"5.7.3"}
-10
View File
@@ -1,10 +0,0 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
outDir: 'html',
emptyOutDir: true,
},
})