skrypt-ai 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/template/next.config.mjs +2 -1
- package/dist/template/package.json +1 -0
- package/dist/template/src/app/docs/[...slug]/page.tsx +4 -0
- package/dist/template/src/app/layout.tsx +15 -7
- package/dist/template/src/app/page.tsx +61 -11
- package/dist/template/src/components/breadcrumbs.tsx +10 -10
- package/dist/template/src/components/docs-layout.tsx +62 -2
- package/dist/template/src/components/header.tsx +30 -18
- package/dist/template/src/components/mdx/accordion.tsx +18 -15
- package/dist/template/src/components/mdx/callout.tsx +50 -37
- package/dist/template/src/components/mdx/card.tsx +9 -9
- package/dist/template/src/components/mdx/code-group.tsx +31 -18
- package/dist/template/src/components/mdx/highlighted-code.tsx +62 -17
- package/dist/template/src/components/mdx/steps.tsx +6 -6
- package/dist/template/src/components/mdx/tabs.tsx +3 -3
- package/dist/template/src/components/search-dialog.tsx +79 -39
- package/dist/template/src/components/sidebar.tsx +13 -12
- package/dist/template/src/components/table-of-contents.tsx +7 -7
- package/dist/template/src/styles/globals.css +240 -68
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -18,7 +18,7 @@ import { loginCommand, logoutCommand, whoamiCommand } from './commands/login.js'
|
|
|
18
18
|
import { cronCommand } from './commands/cron.js';
|
|
19
19
|
import { deployCommand } from './commands/deploy.js';
|
|
20
20
|
import { testCommand } from './commands/test.js';
|
|
21
|
-
const VERSION = '0.3.
|
|
21
|
+
const VERSION = '0.3.4';
|
|
22
22
|
async function checkForUpdates() {
|
|
23
23
|
try {
|
|
24
24
|
const res = await fetch('https://registry.npmjs.org/skrypt-ai/latest', {
|
|
@@ -2,6 +2,7 @@ import { notFound } from 'next/navigation'
|
|
|
2
2
|
import { readFile, readdir, stat } from 'fs/promises'
|
|
3
3
|
import { join } from 'path'
|
|
4
4
|
import { compileMDX } from 'next-mdx-remote/rsc'
|
|
5
|
+
import remarkGfm from 'remark-gfm'
|
|
5
6
|
import * as components from '@/components/mdx'
|
|
6
7
|
|
|
7
8
|
interface PageProps {
|
|
@@ -39,6 +40,9 @@ export default async function DocPage({ params }: PageProps) {
|
|
|
39
40
|
components: components as any,
|
|
40
41
|
options: {
|
|
41
42
|
parseFrontmatter: true,
|
|
43
|
+
mdxOptions: {
|
|
44
|
+
remarkPlugins: [remarkGfm],
|
|
45
|
+
},
|
|
42
46
|
},
|
|
43
47
|
})
|
|
44
48
|
|
|
@@ -2,18 +2,27 @@ import type { Metadata } from 'next'
|
|
|
2
2
|
import { Inter } from 'next/font/google'
|
|
3
3
|
import '@/styles/globals.css'
|
|
4
4
|
import { SyntaxThemeProvider } from '@/contexts/syntax-theme'
|
|
5
|
+
import { readFileSync } from 'fs'
|
|
6
|
+
import { join } from 'path'
|
|
5
7
|
|
|
6
8
|
const inter = Inter({ subsets: ['latin'] })
|
|
7
9
|
|
|
10
|
+
function getDocsConfig() {
|
|
11
|
+
const configPath = join(process.cwd(), 'docs.json')
|
|
12
|
+
return JSON.parse(readFileSync(configPath, 'utf-8'))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const docsConfig = getDocsConfig()
|
|
8
16
|
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://docs.example.com'
|
|
9
|
-
const siteName =
|
|
17
|
+
const siteName = docsConfig.name || 'Documentation'
|
|
18
|
+
const siteDescription = docsConfig.description || 'Documentation'
|
|
10
19
|
|
|
11
20
|
export const metadata: Metadata = {
|
|
12
21
|
title: {
|
|
13
22
|
default: siteName,
|
|
14
23
|
template: `%s | ${siteName}`,
|
|
15
24
|
},
|
|
16
|
-
description:
|
|
25
|
+
description: siteDescription,
|
|
17
26
|
metadataBase: new URL(siteUrl),
|
|
18
27
|
openGraph: {
|
|
19
28
|
type: 'website',
|
|
@@ -21,12 +30,12 @@ export const metadata: Metadata = {
|
|
|
21
30
|
url: siteUrl,
|
|
22
31
|
siteName,
|
|
23
32
|
title: siteName,
|
|
24
|
-
description:
|
|
33
|
+
description: siteDescription,
|
|
25
34
|
},
|
|
26
35
|
twitter: {
|
|
27
36
|
card: 'summary_large_image',
|
|
28
37
|
title: siteName,
|
|
29
|
-
description:
|
|
38
|
+
description: siteDescription,
|
|
30
39
|
},
|
|
31
40
|
robots: {
|
|
32
41
|
index: true,
|
|
@@ -34,13 +43,12 @@ export const metadata: Metadata = {
|
|
|
34
43
|
},
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
// JSON-LD structured data
|
|
38
46
|
const jsonLd = {
|
|
39
47
|
'@context': 'https://schema.org',
|
|
40
48
|
'@type': 'WebSite',
|
|
41
49
|
name: siteName,
|
|
42
50
|
url: siteUrl,
|
|
43
|
-
description:
|
|
51
|
+
description: siteDescription,
|
|
44
52
|
potentialAction: {
|
|
45
53
|
'@type': 'SearchAction',
|
|
46
54
|
target: `${siteUrl}/docs?q={search_term_string}`,
|
|
@@ -61,7 +69,7 @@ export default function RootLayout({
|
|
|
61
69
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
|
62
70
|
/>
|
|
63
71
|
</head>
|
|
64
|
-
<body className={inter.className}>
|
|
72
|
+
<body className={inter.className} suppressHydrationWarning>
|
|
65
73
|
<SyntaxThemeProvider>
|
|
66
74
|
{children}
|
|
67
75
|
</SyntaxThemeProvider>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Link from 'next/link'
|
|
2
2
|
import { readFileSync } from 'fs'
|
|
3
3
|
import { join } from 'path'
|
|
4
|
+
import { ArrowRight, BookOpen, Zap, Code } from 'lucide-react'
|
|
4
5
|
|
|
5
6
|
function getDocsConfig() {
|
|
6
7
|
const configPath = join(process.cwd(), 'docs.json')
|
|
@@ -10,19 +11,68 @@ function getDocsConfig() {
|
|
|
10
11
|
|
|
11
12
|
export default function Home() {
|
|
12
13
|
const docsConfig = getDocsConfig()
|
|
14
|
+
const firstPage = docsConfig.navigation?.[0]?.pages?.[0]?.path || '/docs'
|
|
13
15
|
|
|
14
16
|
return (
|
|
15
|
-
<main className="min-h-screen flex flex-col
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
<main className="min-h-screen flex flex-col">
|
|
18
|
+
{/* Header */}
|
|
19
|
+
<header className="h-[var(--header-height)] border-b border-[var(--color-border)] flex items-center px-6">
|
|
20
|
+
<Link href="/" className="font-semibold text-[0.9375rem] tracking-tight text-[var(--color-text)] hover:no-underline">
|
|
21
|
+
{docsConfig.name}
|
|
22
|
+
</Link>
|
|
23
|
+
</header>
|
|
24
|
+
|
|
25
|
+
{/* Hero */}
|
|
26
|
+
<div className="flex-1 flex flex-col items-center justify-center px-6 py-20">
|
|
27
|
+
<div className="max-w-2xl mx-auto text-center">
|
|
28
|
+
<h1 className="text-4xl sm:text-5xl font-bold tracking-tight text-[var(--color-text)] mb-4">
|
|
29
|
+
{docsConfig.name}
|
|
30
|
+
</h1>
|
|
31
|
+
<p className="text-lg text-[var(--color-text-secondary)] mb-8 max-w-lg mx-auto leading-relaxed">
|
|
32
|
+
{docsConfig.description}
|
|
33
|
+
</p>
|
|
34
|
+
<div className="flex items-center justify-center gap-3">
|
|
35
|
+
<Link
|
|
36
|
+
href={firstPage}
|
|
37
|
+
className="inline-flex items-center gap-2 px-5 py-2.5 bg-[var(--color-text)] text-[var(--color-bg)] rounded-lg font-medium text-[0.875rem] hover:opacity-90 hover:no-underline transition-opacity"
|
|
38
|
+
>
|
|
39
|
+
Get Started
|
|
40
|
+
<ArrowRight size={16} />
|
|
41
|
+
</Link>
|
|
42
|
+
<Link
|
|
43
|
+
href="/docs"
|
|
44
|
+
className="inline-flex items-center gap-2 px-5 py-2.5 border border-[var(--color-border)] text-[var(--color-text)] rounded-lg font-medium text-[0.875rem] hover:bg-[var(--color-bg-secondary)] hover:no-underline transition-colors"
|
|
45
|
+
>
|
|
46
|
+
Documentation
|
|
47
|
+
</Link>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
{/* Feature cards */}
|
|
52
|
+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 max-w-2xl mx-auto mt-16 w-full">
|
|
53
|
+
<div className="p-5 rounded-xl border border-[var(--color-border)] hover:border-[var(--color-border-strong)] transition-colors">
|
|
54
|
+
<BookOpen size={18} className="text-[var(--color-text-tertiary)] mb-3" />
|
|
55
|
+
<h3 className="text-[0.875rem] font-semibold text-[var(--color-text)] mb-1">Guides</h3>
|
|
56
|
+
<p className="text-[0.8125rem] text-[var(--color-text-tertiary)] leading-relaxed">
|
|
57
|
+
Step-by-step tutorials and explanations.
|
|
58
|
+
</p>
|
|
59
|
+
</div>
|
|
60
|
+
<div className="p-5 rounded-xl border border-[var(--color-border)] hover:border-[var(--color-border-strong)] transition-colors">
|
|
61
|
+
<Code size={18} className="text-[var(--color-text-tertiary)] mb-3" />
|
|
62
|
+
<h3 className="text-[0.875rem] font-semibold text-[var(--color-text)] mb-1">API Reference</h3>
|
|
63
|
+
<p className="text-[0.8125rem] text-[var(--color-text-tertiary)] leading-relaxed">
|
|
64
|
+
Complete API documentation with examples.
|
|
65
|
+
</p>
|
|
66
|
+
</div>
|
|
67
|
+
<div className="p-5 rounded-xl border border-[var(--color-border)] hover:border-[var(--color-border-strong)] transition-colors">
|
|
68
|
+
<Zap size={18} className="text-[var(--color-text-tertiary)] mb-3" />
|
|
69
|
+
<h3 className="text-[0.875rem] font-semibold text-[var(--color-text)] mb-1">Quick Start</h3>
|
|
70
|
+
<p className="text-[0.8125rem] text-[var(--color-text-tertiary)] leading-relaxed">
|
|
71
|
+
Get up and running in minutes.
|
|
72
|
+
</p>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
26
76
|
</main>
|
|
27
77
|
)
|
|
28
78
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import Link from 'next/link'
|
|
4
4
|
import { usePathname } from 'next/navigation'
|
|
5
|
-
import { ChevronRight
|
|
5
|
+
import { ChevronRight } from 'lucide-react'
|
|
6
6
|
|
|
7
7
|
export function Breadcrumbs() {
|
|
8
8
|
const pathname = usePathname()
|
|
@@ -20,17 +20,17 @@ export function Breadcrumbs() {
|
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
return (
|
|
23
|
-
<nav className="flex items-center gap-1
|
|
24
|
-
<Link href="/" className="hover:text-[var(--color-text)]">
|
|
25
|
-
|
|
23
|
+
<nav className="flex items-center gap-1 text-[0.8125rem] text-[var(--color-text-tertiary)] mb-6">
|
|
24
|
+
<Link href="/" className="hover:text-[var(--color-text)] hover:no-underline">
|
|
25
|
+
Docs
|
|
26
26
|
</Link>
|
|
27
|
-
{crumbs.map((crumb, i) => (
|
|
28
|
-
<span key={crumb.href} className="flex items-center gap-1
|
|
29
|
-
<ChevronRight size={
|
|
30
|
-
{i === crumbs.length -
|
|
31
|
-
<span className="text-[var(--color-text)]">{crumb.label}</span>
|
|
27
|
+
{crumbs.slice(1).map((crumb, i) => (
|
|
28
|
+
<span key={crumb.href} className="flex items-center gap-1">
|
|
29
|
+
<ChevronRight size={12} className="text-[var(--color-text-tertiary)]" />
|
|
30
|
+
{i === crumbs.length - 2 ? (
|
|
31
|
+
<span className="text-[var(--color-text-secondary)]">{crumb.label}</span>
|
|
32
32
|
) : (
|
|
33
|
-
<Link href={crumb.href} className="hover:text-[var(--color-text)]">
|
|
33
|
+
<Link href={crumb.href} className="hover:text-[var(--color-text)] hover:no-underline">
|
|
34
34
|
{crumb.label}
|
|
35
35
|
</Link>
|
|
36
36
|
)}
|
|
@@ -1,18 +1,75 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react'
|
|
4
|
+
import { usePathname } from 'next/navigation'
|
|
5
|
+
import Link from 'next/link'
|
|
6
|
+
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
|
4
7
|
import { Sidebar } from './sidebar'
|
|
5
8
|
import { Header } from './header'
|
|
6
9
|
import { TableOfContents } from './table-of-contents'
|
|
7
10
|
import { Breadcrumbs } from './breadcrumbs'
|
|
8
11
|
|
|
9
12
|
interface DocsConfig {
|
|
13
|
+
name?: string
|
|
14
|
+
headerLinks?: Array<{ title: string; path: string }>
|
|
10
15
|
navigation: Array<{
|
|
11
16
|
group: string
|
|
12
17
|
pages: Array<{ title: string; path: string }>
|
|
13
18
|
}>
|
|
14
19
|
}
|
|
15
20
|
|
|
21
|
+
function getAllPages(config: DocsConfig): Array<{ title: string; path: string }> {
|
|
22
|
+
return config.navigation.flatMap((group) => group.pages)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function PrevNextNav({ docsConfig }: { docsConfig: DocsConfig }) {
|
|
26
|
+
const pathname = usePathname()
|
|
27
|
+
const allPages = getAllPages(docsConfig)
|
|
28
|
+
const currentIndex = allPages.findIndex((p) => p.path === pathname)
|
|
29
|
+
|
|
30
|
+
if (currentIndex === -1) return null
|
|
31
|
+
|
|
32
|
+
const prev = currentIndex > 0 ? allPages[currentIndex - 1] : null
|
|
33
|
+
const next = currentIndex < allPages.length - 1 ? allPages[currentIndex + 1] : null
|
|
34
|
+
|
|
35
|
+
if (!prev && !next) return null
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div className="mt-12 pt-6 border-t border-[var(--color-border)] grid grid-cols-2 gap-4">
|
|
39
|
+
{prev ? (
|
|
40
|
+
<Link
|
|
41
|
+
href={prev.path}
|
|
42
|
+
className="group flex flex-col gap-1 p-4 rounded-xl border border-[var(--color-border)] hover:border-[var(--color-border-strong)] hover:bg-[var(--color-bg-secondary)] hover:no-underline transition-all"
|
|
43
|
+
>
|
|
44
|
+
<span className="flex items-center gap-1 text-xs text-[var(--color-text-tertiary)]">
|
|
45
|
+
<ChevronLeft size={12} />
|
|
46
|
+
Previous
|
|
47
|
+
</span>
|
|
48
|
+
<span className="text-sm font-medium text-[var(--color-text)] group-hover:text-[var(--color-primary)]">
|
|
49
|
+
{prev.title}
|
|
50
|
+
</span>
|
|
51
|
+
</Link>
|
|
52
|
+
) : (
|
|
53
|
+
<div />
|
|
54
|
+
)}
|
|
55
|
+
{next && (
|
|
56
|
+
<Link
|
|
57
|
+
href={next.path}
|
|
58
|
+
className="group flex flex-col items-end gap-1 p-4 rounded-xl border border-[var(--color-border)] hover:border-[var(--color-border-strong)] hover:bg-[var(--color-bg-secondary)] hover:no-underline transition-all"
|
|
59
|
+
>
|
|
60
|
+
<span className="flex items-center gap-1 text-xs text-[var(--color-text-tertiary)]">
|
|
61
|
+
Next
|
|
62
|
+
<ChevronRight size={12} />
|
|
63
|
+
</span>
|
|
64
|
+
<span className="text-sm font-medium text-[var(--color-text)] group-hover:text-[var(--color-primary)]">
|
|
65
|
+
{next.title}
|
|
66
|
+
</span>
|
|
67
|
+
</Link>
|
|
68
|
+
)}
|
|
69
|
+
</div>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
16
73
|
export function DocsLayout({ children, docsConfig }: { children: React.ReactNode; docsConfig: DocsConfig }) {
|
|
17
74
|
const [menuOpen, setMenuOpen] = useState(false)
|
|
18
75
|
|
|
@@ -21,6 +78,8 @@ export function DocsLayout({ children, docsConfig }: { children: React.ReactNode
|
|
|
21
78
|
<Header
|
|
22
79
|
onMenuToggle={() => setMenuOpen(!menuOpen)}
|
|
23
80
|
menuOpen={menuOpen}
|
|
81
|
+
siteName={docsConfig.name}
|
|
82
|
+
navLinks={docsConfig.headerLinks}
|
|
24
83
|
/>
|
|
25
84
|
<div className="flex">
|
|
26
85
|
<Sidebar
|
|
@@ -28,12 +87,13 @@ export function DocsLayout({ children, docsConfig }: { children: React.ReactNode
|
|
|
28
87
|
onClose={() => setMenuOpen(false)}
|
|
29
88
|
docsConfig={docsConfig}
|
|
30
89
|
/>
|
|
31
|
-
<main className="flex-1 min-w-0 px-
|
|
32
|
-
<div className="max-w-
|
|
90
|
+
<main className="flex-1 min-w-0 px-6 md:px-10 py-8 md:ml-[var(--sidebar-width)] xl:mr-[var(--toc-width)]">
|
|
91
|
+
<div className="max-w-[var(--content-max-width)] mx-auto">
|
|
33
92
|
<Breadcrumbs />
|
|
34
93
|
<article className="prose">
|
|
35
94
|
{children}
|
|
36
95
|
</article>
|
|
96
|
+
<PrevNextNav docsConfig={docsConfig} />
|
|
37
97
|
</div>
|
|
38
98
|
</main>
|
|
39
99
|
<TableOfContents />
|
|
@@ -10,47 +10,59 @@ import { SyntaxThemeSelector } from './syntax-theme-selector'
|
|
|
10
10
|
interface HeaderProps {
|
|
11
11
|
onMenuToggle?: () => void
|
|
12
12
|
menuOpen?: boolean
|
|
13
|
+
siteName?: string
|
|
14
|
+
navLinks?: Array<{ title: string; path: string }>
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
export function Header({ onMenuToggle, menuOpen }: HeaderProps) {
|
|
17
|
+
export function Header({ onMenuToggle, menuOpen, siteName = 'Docs', navLinks }: HeaderProps) {
|
|
16
18
|
const [searchOpen, setSearchOpen] = useState(false)
|
|
17
19
|
|
|
18
20
|
return (
|
|
19
21
|
<>
|
|
20
|
-
<header className="sticky top-0 z-50 border-b border-[var(--color-border)] bg-[var(--color-bg)]/
|
|
21
|
-
<div className="flex items-center justify-between h-
|
|
22
|
-
<div className="flex items-center gap-
|
|
22
|
+
<header className="sticky top-0 z-50 h-[var(--header-height)] border-b border-[var(--color-border)] bg-[var(--color-bg)]/80 backdrop-blur-xl">
|
|
23
|
+
<div className="flex items-center justify-between h-full px-4 md:px-6">
|
|
24
|
+
<div className="flex items-center gap-6">
|
|
23
25
|
{/* Mobile menu button */}
|
|
24
26
|
<button
|
|
25
27
|
onClick={onMenuToggle}
|
|
26
|
-
className="md:hidden p-
|
|
28
|
+
className="md:hidden p-1.5 -ml-1.5 rounded-lg text-[var(--color-text-secondary)] hover:text-[var(--color-text)] hover:bg-[var(--color-bg-secondary)]"
|
|
27
29
|
aria-label={menuOpen ? 'Close menu' : 'Open menu'}
|
|
28
30
|
aria-expanded={menuOpen}
|
|
29
31
|
>
|
|
30
|
-
{menuOpen ? <X size={
|
|
32
|
+
{menuOpen ? <X size={18} /> : <Menu size={18} />}
|
|
31
33
|
</button>
|
|
32
34
|
|
|
33
|
-
<Link href="/" className="font-semibold text-
|
|
34
|
-
|
|
35
|
+
<Link href="/" className="font-semibold text-[0.9375rem] tracking-tight text-[var(--color-text)] hover:no-underline">
|
|
36
|
+
{siteName}
|
|
35
37
|
</Link>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
|
|
39
|
+
<nav className="hidden md:flex items-center gap-1">
|
|
40
|
+
<Link
|
|
41
|
+
href="/docs"
|
|
42
|
+
className="px-3 py-1.5 text-[0.8125rem] text-[var(--color-text-secondary)] hover:text-[var(--color-text)] hover:bg-[var(--color-bg-secondary)] rounded-lg hover:no-underline"
|
|
43
|
+
>
|
|
38
44
|
Documentation
|
|
39
45
|
</Link>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
{navLinks?.map((link) => (
|
|
47
|
+
<Link
|
|
48
|
+
key={link.path}
|
|
49
|
+
href={link.path}
|
|
50
|
+
className="px-3 py-1.5 text-[0.8125rem] text-[var(--color-text-secondary)] hover:text-[var(--color-text)] hover:bg-[var(--color-bg-secondary)] rounded-lg hover:no-underline"
|
|
51
|
+
>
|
|
52
|
+
{link.title}
|
|
53
|
+
</Link>
|
|
54
|
+
))}
|
|
43
55
|
</nav>
|
|
44
56
|
</div>
|
|
45
57
|
|
|
46
|
-
<div className="flex items-center gap-
|
|
58
|
+
<div className="flex items-center gap-1.5">
|
|
47
59
|
<button
|
|
48
60
|
onClick={() => setSearchOpen(true)}
|
|
49
|
-
className="flex items-center gap-2 px-3 py-1.5 text-
|
|
61
|
+
className="flex items-center gap-2 px-3 py-1.5 text-[0.8125rem] text-[var(--color-text-tertiary)] bg-[var(--color-bg-secondary)] border border-[var(--color-border)] rounded-lg hover:border-[var(--color-border-strong)] hover:text-[var(--color-text-secondary)]"
|
|
50
62
|
>
|
|
51
|
-
<Search size={
|
|
52
|
-
<span className="hidden sm:inline">Search
|
|
53
|
-
<kbd className="hidden sm:inline px-1.5 py-0.5 text-
|
|
63
|
+
<Search size={14} />
|
|
64
|
+
<span className="hidden sm:inline">Search...</span>
|
|
65
|
+
<kbd className="hidden sm:inline ml-2 px-1.5 py-0.5 text-[0.6875rem] font-medium bg-[var(--color-bg)] border border-[var(--color-border)] rounded">
|
|
54
66
|
⌘K
|
|
55
67
|
</kbd>
|
|
56
68
|
</button>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useState, ReactNode } from 'react'
|
|
4
|
-
import {
|
|
4
|
+
import { ChevronRight } from 'lucide-react'
|
|
5
5
|
import { cn } from '@/lib/utils'
|
|
6
6
|
|
|
7
7
|
interface AccordionProps {
|
|
@@ -14,27 +14,30 @@ export function Accordion({ title, defaultOpen = false, children }: AccordionPro
|
|
|
14
14
|
const [open, setOpen] = useState(defaultOpen)
|
|
15
15
|
|
|
16
16
|
return (
|
|
17
|
-
<div className="border border-[var(--color-border)]
|
|
17
|
+
<div className="border-b border-[var(--color-border)] last:border-b-0">
|
|
18
18
|
<button
|
|
19
19
|
onClick={() => setOpen(!open)}
|
|
20
|
-
className="flex items-center
|
|
20
|
+
className="flex items-center gap-2 w-full py-3.5 text-left text-[0.875rem] font-medium text-[var(--color-text)] hover:text-[var(--color-primary)] transition-colors"
|
|
21
21
|
>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
size={20}
|
|
22
|
+
<ChevronRight
|
|
23
|
+
size={14}
|
|
25
24
|
className={cn(
|
|
26
|
-
'text-[var(--color-text-tertiary)] transition-transform',
|
|
27
|
-
open && 'rotate-
|
|
25
|
+
'text-[var(--color-text-tertiary)] transition-transform duration-200 shrink-0',
|
|
26
|
+
open && 'rotate-90'
|
|
28
27
|
)}
|
|
29
28
|
/>
|
|
29
|
+
{title}
|
|
30
30
|
</button>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
<div
|
|
32
|
+
className={cn(
|
|
33
|
+
'overflow-hidden transition-all duration-200',
|
|
34
|
+
open ? 'max-h-[2000px] opacity-100' : 'max-h-0 opacity-0'
|
|
35
|
+
)}
|
|
36
|
+
>
|
|
37
|
+
<div className="pl-5 pb-4 text-[0.8125rem] text-[var(--color-text-secondary)] leading-relaxed">
|
|
38
|
+
{children}
|
|
36
39
|
</div>
|
|
37
|
-
|
|
40
|
+
</div>
|
|
38
41
|
</div>
|
|
39
42
|
)
|
|
40
43
|
}
|
|
@@ -44,5 +47,5 @@ interface AccordionGroupProps {
|
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
export function AccordionGroup({ children }: AccordionGroupProps) {
|
|
47
|
-
return <div className="my-6
|
|
50
|
+
return <div className="my-6 border-t border-[var(--color-border)]">{children}</div>
|
|
48
51
|
}
|
|
@@ -18,74 +18,87 @@ interface CalloutProps {
|
|
|
18
18
|
|
|
19
19
|
const config: Record<CalloutType, {
|
|
20
20
|
icon: typeof InfoIcon
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
borderColor: string
|
|
22
|
+
iconColor: string
|
|
23
|
+
bgColor: string
|
|
24
|
+
titleColor: string
|
|
25
|
+
textColor: string
|
|
26
|
+
defaultTitle: string
|
|
25
27
|
}> = {
|
|
26
28
|
info: {
|
|
27
29
|
icon: InfoIcon,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
borderColor: 'border-l-blue-500',
|
|
31
|
+
iconColor: 'text-blue-500',
|
|
32
|
+
bgColor: 'bg-blue-500/5',
|
|
33
|
+
titleColor: 'text-blue-600 dark:text-blue-400',
|
|
34
|
+
textColor: 'text-[var(--color-text-secondary)]',
|
|
35
|
+
defaultTitle: 'Info',
|
|
32
36
|
},
|
|
33
37
|
warning: {
|
|
34
38
|
icon: AlertTriangle,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
borderColor: 'border-l-amber-500',
|
|
40
|
+
iconColor: 'text-amber-500',
|
|
41
|
+
bgColor: 'bg-amber-500/5',
|
|
42
|
+
titleColor: 'text-amber-600 dark:text-amber-400',
|
|
43
|
+
textColor: 'text-[var(--color-text-secondary)]',
|
|
44
|
+
defaultTitle: 'Warning',
|
|
39
45
|
},
|
|
40
46
|
success: {
|
|
41
47
|
icon: CheckCircle,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
borderColor: 'border-l-emerald-500',
|
|
49
|
+
iconColor: 'text-emerald-500',
|
|
50
|
+
bgColor: 'bg-emerald-500/5',
|
|
51
|
+
titleColor: 'text-emerald-600 dark:text-emerald-400',
|
|
52
|
+
textColor: 'text-[var(--color-text-secondary)]',
|
|
53
|
+
defaultTitle: 'Success',
|
|
46
54
|
},
|
|
47
55
|
error: {
|
|
48
56
|
icon: XCircle,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
borderColor: 'border-l-red-500',
|
|
58
|
+
iconColor: 'text-red-500',
|
|
59
|
+
bgColor: 'bg-red-500/5',
|
|
60
|
+
titleColor: 'text-red-600 dark:text-red-400',
|
|
61
|
+
textColor: 'text-[var(--color-text-secondary)]',
|
|
62
|
+
defaultTitle: 'Error',
|
|
53
63
|
},
|
|
54
64
|
tip: {
|
|
55
65
|
icon: Lightbulb,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
borderColor: 'border-l-violet-500',
|
|
67
|
+
iconColor: 'text-violet-500',
|
|
68
|
+
bgColor: 'bg-violet-500/5',
|
|
69
|
+
titleColor: 'text-violet-600 dark:text-violet-400',
|
|
70
|
+
textColor: 'text-[var(--color-text-secondary)]',
|
|
71
|
+
defaultTitle: 'Tip',
|
|
60
72
|
},
|
|
61
73
|
note: {
|
|
62
74
|
icon: InfoIcon,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
borderColor: 'border-l-[var(--color-border-strong)]',
|
|
76
|
+
iconColor: 'text-[var(--color-text-tertiary)]',
|
|
77
|
+
bgColor: 'bg-[var(--color-bg-secondary)]',
|
|
78
|
+
titleColor: 'text-[var(--color-text)]',
|
|
79
|
+
textColor: 'text-[var(--color-text-secondary)]',
|
|
80
|
+
defaultTitle: 'Note',
|
|
67
81
|
},
|
|
68
82
|
}
|
|
69
83
|
|
|
70
84
|
export function Callout({ type = 'info', title, children }: CalloutProps) {
|
|
71
|
-
const { icon: Icon,
|
|
85
|
+
const { icon: Icon, borderColor, iconColor, bgColor, titleColor, textColor, defaultTitle } = config[type]
|
|
72
86
|
|
|
73
87
|
return (
|
|
74
|
-
<div className={cn('my-6
|
|
88
|
+
<div className={cn('my-6 rounded-lg border-l-4 py-3 px-4', borderColor, bgColor)}>
|
|
75
89
|
<div className="flex gap-3">
|
|
76
|
-
<Icon className={cn('w-
|
|
77
|
-
<div>
|
|
78
|
-
{
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<div className={cn('text-
|
|
90
|
+
<Icon className={cn('w-[18px] h-[18px] mt-0.5 flex-shrink-0', iconColor)} />
|
|
91
|
+
<div className="min-w-0">
|
|
92
|
+
<div className={cn('text-[0.8125rem] font-semibold mb-0.5', titleColor)}>
|
|
93
|
+
{title || defaultTitle}
|
|
94
|
+
</div>
|
|
95
|
+
<div className={cn('text-[0.8125rem] leading-relaxed [&>p]:my-1', textColor)}>{children}</div>
|
|
82
96
|
</div>
|
|
83
97
|
</div>
|
|
84
98
|
</div>
|
|
85
99
|
)
|
|
86
100
|
}
|
|
87
101
|
|
|
88
|
-
// Convenience components
|
|
89
102
|
export function Info(props: Omit<CalloutProps, 'type'>) {
|
|
90
103
|
return <Callout type="info" {...props} />
|
|
91
104
|
}
|