mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-28 11:25:00 +00:00
* fix: keep download counts consistent across skill pages * fix: stabilize empty search ordering across sorts * fix: show disabled-account reason on login redirect * fix: mute report input placeholder text * fix: return skill detail to my skills page * test: stabilize auth context filter coverage * feat(publish): increase single file limit to 10MB * feat(publish): expand allowed file extensions * feat(publish): extend secret scanning to new text file types * feat(publish): add content validation for new file types * refactor(publish): inject configurable limits into SkillPackageArchiveExtractor * feat(publish): support zip with single root directory wrapper * feat(publish): expand determineContentType for new file types * test(publish): update tests for new upload constraints * fix(web): add REJECTED status label and styling to my-skills page The resolveStatusLabel and resolveStatusClassName functions were missing the REJECTED case, causing rejected skills to show raw status string with no color styling. * fix: prevent deleting the last remaining version of a skill Backend: added version count check in SkillGovernanceService.deleteVersion() Frontend: hide delete button when only one version remains * test: fix and add tests for last-version deletion guard - Fix deleteVersion_removesDraftFilesAndBundle: mock findBySkillId to return 2 versions so the new guard doesn't block the happy path - Add deleteVersion_rejectsLastRemainingVersion: verify that deleting the only remaining version is rejected with the correct error code ---------
482 lines
10 KiB
CSS
482 lines
10 KiB
CSS
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
@layer base {
|
|
:root {
|
|
/* SkillHub — 浅色主题 (indigo-violet brand) */
|
|
--background: 210 20% 98%;
|
|
--foreground: 220 26% 14%;
|
|
--card: 0 0% 100%;
|
|
--card-foreground: 220 26% 14%;
|
|
--popover: 0 0% 100%;
|
|
--popover-foreground: 220 26% 14%;
|
|
/* Indigo primary (#6A6DFF) */
|
|
--primary: 239 100% 71%;
|
|
--primary-foreground: 0 0% 100%;
|
|
/* Surface tones */
|
|
--secondary: 210 30% 96%;
|
|
--secondary-foreground: 220 26% 14%;
|
|
--muted: 210 25% 95%;
|
|
--muted-foreground: 215 14% 46%;
|
|
/* Violet accent (#B85EFF) */
|
|
--accent: 271 100% 68%;
|
|
--accent-foreground: 0 0% 100%;
|
|
--destructive: 0 72% 55%;
|
|
--destructive-foreground: 0 0% 100%;
|
|
--border: 214 32% 91%;
|
|
--input: 214 32% 91%;
|
|
--ring: 239 100% 71%;
|
|
--radius: 0.75rem;
|
|
|
|
/* Extended palette */
|
|
--surface-glass: 210 30% 97%;
|
|
--glow-primary: 239 100% 71%;
|
|
--glow-accent: 271 100% 68%;
|
|
--success: 160 60% 45%;
|
|
--warning: 38 92% 58%;
|
|
|
|
/* Brand */
|
|
--brand-start: #6A6DFF;
|
|
--brand-end: #B85EFF;
|
|
--brand-gradient: linear-gradient(135deg, #6A6DFF 0%, #B85EFF 100%);
|
|
|
|
/* Text semantic */
|
|
--text-secondary: 215 19% 35%;
|
|
--text-muted: 215 14% 46%;
|
|
--text-placeholder: 213 12% 63%;
|
|
|
|
/* Border semantic */
|
|
--border-card: 220 26% 94%;
|
|
}
|
|
|
|
.dark {
|
|
/* Dark theme (preserved, not default) */
|
|
--background: 222 47% 6%;
|
|
--foreground: 210 40% 96%;
|
|
--card: 222 40% 9%;
|
|
--card-foreground: 210 40% 96%;
|
|
--popover: 222 40% 9%;
|
|
--popover-foreground: 210 40% 96%;
|
|
--primary: 239 100% 75%;
|
|
--primary-foreground: 222 47% 6%;
|
|
--secondary: 222 30% 13%;
|
|
--secondary-foreground: 210 30% 85%;
|
|
--muted: 222 25% 15%;
|
|
--muted-foreground: 215 20% 55%;
|
|
--accent: 271 100% 72%;
|
|
--accent-foreground: 0 0% 100%;
|
|
--destructive: 0 72% 55%;
|
|
--destructive-foreground: 0 0% 100%;
|
|
--border: 222 20% 18%;
|
|
--input: 222 20% 18%;
|
|
--ring: 239 100% 75%;
|
|
--surface-glass: 222 35% 11%;
|
|
--glow-primary: 239 100% 75%;
|
|
--glow-accent: 271 100% 72%;
|
|
}
|
|
}
|
|
|
|
@layer base {
|
|
* {
|
|
@apply border-border;
|
|
}
|
|
|
|
html {
|
|
scroll-behavior: smooth;
|
|
}
|
|
|
|
body {
|
|
@apply bg-background text-foreground antialiased;
|
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
}
|
|
|
|
h1, h2, h3, h4, h5, h6 {
|
|
font-family: 'Syne', 'IBM Plex Sans', system-ui, sans-serif;
|
|
}
|
|
|
|
code, pre, kbd {
|
|
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
|
}
|
|
|
|
/* Hide browser-native password reveal button (conflicts with custom toggle) */
|
|
input[type="password"]::-ms-reveal,
|
|
input[type="password"]::-ms-clear,
|
|
input[type="password"]::-webkit-credentials-auto-fill-button {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
/* ─── Dot-grid background texture ─── */
|
|
.bg-dots {
|
|
background-image: radial-gradient(circle, hsl(var(--muted-foreground) / 0.12) 1px, transparent 1px);
|
|
background-size: 24px 24px;
|
|
}
|
|
|
|
/* ─── Gradient glow orbs ─── */
|
|
.glow-orb-primary {
|
|
position: fixed;
|
|
width: 600px;
|
|
height: 600px;
|
|
border-radius: 50%;
|
|
background: radial-gradient(circle, hsl(var(--glow-primary) / 0.08) 0%, transparent 70%);
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
}
|
|
|
|
.glow-orb-accent {
|
|
position: fixed;
|
|
width: 500px;
|
|
height: 500px;
|
|
border-radius: 50%;
|
|
background: radial-gradient(circle, hsl(var(--glow-accent) / 0.06) 0%, transparent 70%);
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
}
|
|
|
|
/* ─── Glass morphism ─── */
|
|
.glass {
|
|
background: hsl(var(--surface-glass) / 0.7);
|
|
backdrop-filter: blur(16px) saturate(1.4);
|
|
-webkit-backdrop-filter: blur(16px) saturate(1.4);
|
|
border: 1px solid hsl(var(--border) / 0.5);
|
|
}
|
|
|
|
.glass-strong {
|
|
background: hsl(var(--surface-glass) / 0.85);
|
|
backdrop-filter: blur(24px) saturate(1.6);
|
|
-webkit-backdrop-filter: blur(24px) saturate(1.6);
|
|
border: 1px solid hsl(var(--border) / 0.6);
|
|
}
|
|
|
|
/* ─── Animations ─── */
|
|
@keyframes fade-up {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes fade-in {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
|
|
@keyframes slide-in-right {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateX(16px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
@keyframes shimmer {
|
|
0% { background-position: -200% 0; }
|
|
100% { background-position: 200% 0; }
|
|
}
|
|
|
|
@keyframes float {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(-8px); }
|
|
}
|
|
|
|
@keyframes pulse-glow {
|
|
0%, 100% { opacity: 0.6; }
|
|
50% { opacity: 1; }
|
|
}
|
|
|
|
.animate-fade-up {
|
|
animation: fade-up 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
}
|
|
|
|
.scroll-fade-up {
|
|
opacity: 0;
|
|
transform: translateY(32px);
|
|
transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
|
|
.scroll-fade-up.in-view {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.animate-fade-in {
|
|
animation: fade-in 0.5s ease both;
|
|
}
|
|
|
|
.animate-slide-in-right {
|
|
animation: slide-in-right 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
}
|
|
|
|
.animate-shimmer {
|
|
background: linear-gradient(
|
|
90deg,
|
|
hsl(var(--muted)) 25%,
|
|
hsl(var(--muted-foreground) / 0.08) 50%,
|
|
hsl(var(--muted)) 75%
|
|
);
|
|
background-size: 200% 100%;
|
|
animation: shimmer 1.8s ease-in-out infinite;
|
|
}
|
|
|
|
.animate-float {
|
|
animation: float 6s ease-in-out infinite;
|
|
}
|
|
|
|
/* Stagger delays */
|
|
.delay-1 { animation-delay: 0.1s; }
|
|
.delay-2 { animation-delay: 0.2s; }
|
|
.delay-3 { animation-delay: 0.3s; }
|
|
.delay-4 { animation-delay: 0.4s; }
|
|
.delay-5 { animation-delay: 0.5s; }
|
|
.delay-6 { animation-delay: 0.6s; }
|
|
|
|
/* ─── Gradient text ─── */
|
|
.text-gradient-primary {
|
|
background: linear-gradient(135deg, hsl(var(--primary)), hsl(var(--accent)));
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.text-gradient-hero {
|
|
background: var(--brand-gradient);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.dark .text-gradient-hero {
|
|
background: linear-gradient(135deg, #8183FF, #C77EFF);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
/* ─── Card hover lift ─── */
|
|
.card-hover {
|
|
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1),
|
|
box-shadow 0.3s cubic-bezier(0.16, 1, 0.3, 1),
|
|
border-color 0.3s ease;
|
|
}
|
|
|
|
.card-hover:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: 0 20px 40px -12px hsl(var(--primary) / 0.1),
|
|
0 8px 16px -8px hsl(0 0% 0% / 0.2);
|
|
border-color: hsl(var(--primary) / 0.3);
|
|
}
|
|
|
|
/* ─── Scrollbar ─── */
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: hsl(var(--background));
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: hsl(var(--muted-foreground) / 0.3);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: hsl(var(--muted-foreground) / 0.5);
|
|
}
|
|
|
|
/* ─── Selection ─── */
|
|
::selection {
|
|
background: hsl(var(--primary) / 0.3);
|
|
color: hsl(var(--foreground));
|
|
}
|
|
|
|
/* ─── Focus ring ─── */
|
|
.focus-ring {
|
|
@apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background;
|
|
}
|
|
|
|
/* ─── Brand gradient utilities ─── */
|
|
.text-brand-gradient {
|
|
background: var(--brand-gradient);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
background-clip: text;
|
|
}
|
|
|
|
.bg-brand-gradient {
|
|
background: var(--brand-gradient);
|
|
}
|
|
|
|
/* ─── Handle tag (namespace @ pill) ─── */
|
|
.handle-tag {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 4px 10px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
color: #0369a1;
|
|
background: #e0f2fe;
|
|
border: 1px solid #7dd3fc;
|
|
border-radius: 9999px;
|
|
}
|
|
|
|
/* ─── Status pills ─── */
|
|
.status-pill {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
margin-left: 5px;
|
|
padding: 4px 18px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
border-radius: 9999px;
|
|
color: white;
|
|
}
|
|
|
|
.status-pill--published { background: #16a34a; }
|
|
.status-pill--review { background: #ea580c; }
|
|
.status-pill--archived { background: #6b7280; }
|
|
.status-pill--rejected { background: #dc2626; }
|
|
|
|
/* ─── Role pill ─── */
|
|
.role-pill {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 4px 10px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
border-radius: 9999px;
|
|
border: 1px solid hsl(var(--border));
|
|
color: hsl(var(--text-secondary));
|
|
}
|
|
|
|
/* ─── Soft badges ─── */
|
|
.badge-soft {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 4px 10px;
|
|
border-radius: 9999px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.badge-soft-green {
|
|
background: #dcfce7;
|
|
color: #166534;
|
|
}
|
|
|
|
.badge-soft-blue {
|
|
background: #dbeafe;
|
|
color: #1d4ed8;
|
|
}
|
|
|
|
/* ─── Tab active ─── */
|
|
.tab-active {
|
|
border-bottom: 2px solid #3b82f6;
|
|
color: #1d4ed8;
|
|
}
|
|
|
|
/* ─── Code block ─── */
|
|
.code-block {
|
|
background: #1A202C;
|
|
color: #e5e7eb;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
/* ─── Markdown code highlighting ─── */
|
|
.hljs {
|
|
display: block;
|
|
color: hsl(var(--foreground) / 0.92);
|
|
background: transparent;
|
|
}
|
|
|
|
.hljs-comment,
|
|
.hljs-quote {
|
|
color: hsl(var(--muted-foreground) / 0.9);
|
|
font-style: italic;
|
|
}
|
|
|
|
.hljs-keyword,
|
|
.hljs-selector-tag,
|
|
.hljs-literal,
|
|
.hljs-name,
|
|
.hljs-section,
|
|
.hljs-link {
|
|
color: hsl(var(--primary) / 0.95);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.hljs-string,
|
|
.hljs-title,
|
|
.hljs-attribute,
|
|
.hljs-symbol,
|
|
.hljs-bullet,
|
|
.hljs-addition {
|
|
color: hsl(var(--accent) / 0.9);
|
|
}
|
|
|
|
.hljs-number,
|
|
.hljs-meta,
|
|
.hljs-variable,
|
|
.hljs-template-variable,
|
|
.hljs-type,
|
|
.hljs-built_in,
|
|
.hljs-params {
|
|
color: hsl(218 70% 45%);
|
|
}
|
|
|
|
.hljs-function,
|
|
.hljs-title.function_,
|
|
.hljs-title.class_ {
|
|
color: hsl(var(--foreground) / 0.95);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.hljs-regexp,
|
|
.hljs-subst,
|
|
.hljs-doctag {
|
|
color: hsl(24 85% 52%);
|
|
}
|
|
|
|
.hljs-deletion {
|
|
color: hsl(var(--destructive) / 0.88);
|
|
}
|
|
|
|
/* ─── Upload zone ─── */
|
|
.upload-zone {
|
|
border: 2px dashed hsl(var(--border));
|
|
background: hsl(var(--background));
|
|
transition: border-color 0.2s ease, background 0.2s ease;
|
|
}
|
|
|
|
.upload-zone:hover {
|
|
border-color: var(--brand-start);
|
|
background: rgba(106, 109, 255, 0.04);
|
|
}
|
|
|
|
.upload-zone .upload-zone-icon {
|
|
color: hsl(var(--muted-foreground));
|
|
transition: color 0.2s ease;
|
|
}
|
|
|
|
.upload-zone:hover .upload-zone-icon {
|
|
color: var(--brand-start);
|
|
}
|
|
|
|
/* ─── Feature icon shadow ─── */
|
|
.feature-icon {
|
|
box-shadow: 0 14px 30px rgba(106, 109, 255, 0.45);
|
|
}
|
|
|
|
/* ─── Hero input placeholder ─── */
|
|
.hero-input::placeholder {
|
|
color: hsl(var(--text-placeholder));
|
|
}
|