skrypt-ai 0.3.2 → 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 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.2';
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', {
@@ -1,9 +1,10 @@
1
1
  import createMDX from '@next/mdx'
2
+ import remarkGfm from 'remark-gfm'
2
3
 
3
4
  const withMDX = createMDX({
4
5
  extension: /\.mdx?$/,
5
6
  options: {
6
- remarkPlugins: [],
7
+ remarkPlugins: [remarkGfm],
7
8
  rehypePlugins: [],
8
9
  },
9
10
  })
@@ -22,6 +22,7 @@
22
22
  "lucide-react": "^0.500.0",
23
23
  "clsx": "^2.1.0",
24
24
  "tailwind-merge": "^3.0.0",
25
+ "remark-gfm": "^4.0.0",
25
26
  "gray-matter": "^4.0.3",
26
27
  "@scalar/nextjs-api-reference": "^0.4.0",
27
28
  "@codesandbox/sandpack-react": "^2.20.0"
@@ -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
 
@@ -1,9 +1,17 @@
1
1
  import { DocsLayout } from '@/components/docs-layout'
2
+ import { readFileSync } from 'fs'
3
+ import { join } from 'path'
4
+
5
+ function getDocsConfig() {
6
+ const configPath = join(process.cwd(), 'docs.json')
7
+ return JSON.parse(readFileSync(configPath, 'utf-8'))
8
+ }
2
9
 
3
10
  export default function Layout({
4
11
  children,
5
12
  }: {
6
13
  children: React.ReactNode
7
14
  }) {
8
- return <DocsLayout>{children}</DocsLayout>
15
+ const docsConfig = getDocsConfig()
16
+ return <DocsLayout docsConfig={docsConfig}>{children}</DocsLayout>
9
17
  }
@@ -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 = process.env.NEXT_PUBLIC_SITE_NAME || 'API Documentation'
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: 'API documentation with interactive examples',
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: 'API documentation with interactive examples',
33
+ description: siteDescription,
25
34
  },
26
35
  twitter: {
27
36
  card: 'summary_large_image',
28
37
  title: siteName,
29
- description: 'API documentation with interactive examples',
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: 'API documentation with interactive examples',
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 items-center justify-center p-8">
16
- <h1 className="text-4xl font-bold mb-4">{docsConfig.name} Documentation</h1>
17
- <p className="text-[var(--color-text-secondary)] mb-8 text-center max-w-xl">
18
- {docsConfig.description}
19
- </p>
20
- <Link
21
- href="/docs"
22
- className="px-6 py-3 bg-[var(--color-primary)] text-white rounded-lg font-medium hover:bg-[var(--color-primary-dark)] transition-colors"
23
- >
24
- Get Started
25
- </Link>
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, Home } from 'lucide-react'
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.5 text-[13px] text-[var(--color-text-tertiary)] mb-4">
24
- <Link href="/" className="hover:text-[var(--color-text)]">
25
- <Home size={14} />
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.5">
29
- <ChevronRight size={14} />
30
- {i === crumbs.length - 1 ? (
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,12 +1,76 @@
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
- export function DocsLayout({ children }: { children: React.ReactNode }) {
12
+ interface DocsConfig {
13
+ name?: string
14
+ headerLinks?: Array<{ title: string; path: string }>
15
+ navigation: Array<{
16
+ group: string
17
+ pages: Array<{ title: string; path: string }>
18
+ }>
19
+ }
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
+
73
+ export function DocsLayout({ children, docsConfig }: { children: React.ReactNode; docsConfig: DocsConfig }) {
10
74
  const [menuOpen, setMenuOpen] = useState(false)
11
75
 
12
76
  return (
@@ -14,18 +78,22 @@ export function DocsLayout({ children }: { children: React.ReactNode }) {
14
78
  <Header
15
79
  onMenuToggle={() => setMenuOpen(!menuOpen)}
16
80
  menuOpen={menuOpen}
81
+ siteName={docsConfig.name}
82
+ navLinks={docsConfig.headerLinks}
17
83
  />
18
84
  <div className="flex">
19
85
  <Sidebar
20
86
  open={menuOpen}
21
87
  onClose={() => setMenuOpen(false)}
88
+ docsConfig={docsConfig}
22
89
  />
23
- <main className="flex-1 min-w-0 px-4 md:px-8 py-6 md:ml-[var(--sidebar-width)] xl:mr-[var(--toc-width)]">
24
- <div className="max-w-3xl mx-auto">
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">
25
92
  <Breadcrumbs />
26
93
  <article className="prose">
27
94
  {children}
28
95
  </article>
96
+ <PrevNextNav docsConfig={docsConfig} />
29
97
  </div>
30
98
  </main>
31
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)]/95 backdrop-blur">
21
- <div className="flex items-center justify-between h-16 px-4 md:px-6">
22
- <div className="flex items-center gap-4">
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-2 -ml-2 text-[var(--color-text-secondary)] hover:text-[var(--color-text)]"
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={20} /> : <Menu size={20} />}
32
+ {menuOpen ? <X size={18} /> : <Menu size={18} />}
31
33
  </button>
32
34
 
33
- <Link href="/" className="font-semibold text-lg">
34
- Docs
35
+ <Link href="/" className="font-semibold text-[0.9375rem] tracking-tight text-[var(--color-text)] hover:no-underline">
36
+ {siteName}
35
37
  </Link>
36
- <nav className="hidden md:flex items-center gap-4">
37
- <Link href="/docs" className="text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)]">
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
- <Link href="/docs/api" className="text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)]">
41
- API Reference
42
- </Link>
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-2">
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-sm text-[var(--color-text-tertiary)] bg-[var(--color-bg-secondary)] border border-[var(--color-border)] rounded-lg hover:border-[var(--color-border-strong)] transition-colors"
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={16} />
52
- <span className="hidden sm:inline">Search docs...</span>
53
- <kbd className="hidden sm:inline px-1.5 py-0.5 text-xs bg-[var(--color-bg-tertiary)] rounded">
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 { ChevronDown } from 'lucide-react'
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)] rounded-lg my-4">
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 justify-between w-full px-4 py-3 text-left font-medium hover:bg-[var(--color-bg-secondary)] transition-colors"
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
- {title}
23
- <ChevronDown
24
- size={20}
22
+ <ChevronRight
23
+ size={14}
25
24
  className={cn(
26
- 'text-[var(--color-text-tertiary)] transition-transform',
27
- open && 'rotate-180'
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
- {open && (
32
- <div className="px-4 pb-4 pt-0 border-t border-[var(--color-border)]">
33
- <div className="pt-3 text-[var(--color-text-secondary)]">
34
- {children}
35
- </div>
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 space-y-2">{children}</div>
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
- bg: string
22
- border: string
23
- title: string
24
- text: string
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
- bg: 'bg-blue-50 dark:bg-blue-950/30',
29
- border: 'border-blue-200 dark:border-blue-800',
30
- title: 'text-blue-800 dark:text-blue-300',
31
- text: 'text-blue-700 dark:text-blue-400',
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
- bg: 'bg-amber-50 dark:bg-amber-950/30',
36
- border: 'border-amber-200 dark:border-amber-800',
37
- title: 'text-amber-800 dark:text-amber-300',
38
- text: 'text-amber-700 dark:text-amber-400',
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
- bg: 'bg-green-50 dark:bg-green-950/30',
43
- border: 'border-green-200 dark:border-green-800',
44
- title: 'text-green-800 dark:text-green-300',
45
- text: 'text-green-700 dark:text-green-400',
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
- bg: 'bg-red-50 dark:bg-red-950/30',
50
- border: 'border-red-200 dark:border-red-800',
51
- title: 'text-red-800 dark:text-red-300',
52
- text: 'text-red-700 dark:text-red-400',
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
- bg: 'bg-purple-50 dark:bg-purple-950/30',
57
- border: 'border-purple-200 dark:border-purple-800',
58
- title: 'text-purple-800 dark:text-purple-300',
59
- text: 'text-purple-700 dark:text-purple-400',
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
- bg: 'bg-gray-50 dark:bg-gray-900/50',
64
- border: 'border-gray-200 dark:border-gray-700',
65
- title: 'text-gray-800 dark:text-gray-200',
66
- text: 'text-gray-700 dark:text-gray-300',
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, bg, border, title: titleColor, text } = config[type]
85
+ const { icon: Icon, borderColor, iconColor, bgColor, titleColor, textColor, defaultTitle } = config[type]
72
86
 
73
87
  return (
74
- <div className={cn('my-6 p-4 border rounded-lg', bg, border)}>
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-5 h-5 mt-0.5 flex-shrink-0', titleColor)} />
77
- <div>
78
- {title && (
79
- <div className={cn('font-medium mb-1', titleColor)}>{title}</div>
80
- )}
81
- <div className={cn('text-sm', text)}>{children}</div>
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
  }