skrypt-ai 0.3.3 → 0.4.0

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.
Files changed (97) hide show
  1. package/README.md +1 -1
  2. package/dist/auth/index.d.ts +0 -1
  3. package/dist/auth/index.js +3 -5
  4. package/dist/autofix/index.js +15 -3
  5. package/dist/cli.js +19 -4
  6. package/dist/commands/check-links.js +164 -174
  7. package/dist/commands/deploy.js +5 -2
  8. package/dist/commands/generate.js +206 -199
  9. package/dist/commands/i18n.js +3 -20
  10. package/dist/commands/init.js +47 -40
  11. package/dist/commands/lint.js +3 -20
  12. package/dist/commands/mcp.js +125 -122
  13. package/dist/commands/monitor.js +125 -108
  14. package/dist/commands/review-pr.js +1 -1
  15. package/dist/commands/sdk.js +1 -1
  16. package/dist/config/loader.js +21 -2
  17. package/dist/generator/organizer.d.ts +3 -0
  18. package/dist/generator/organizer.js +4 -9
  19. package/dist/generator/writer.js +2 -10
  20. package/dist/github/pr-comments.js +21 -8
  21. package/dist/plugins/index.js +1 -0
  22. package/dist/scanner/index.js +8 -2
  23. package/dist/template/docs.json +2 -1
  24. package/dist/template/next.config.mjs +3 -1
  25. package/dist/template/package.json +17 -14
  26. package/dist/template/public/favicon.svg +4 -0
  27. package/dist/template/public/search-index.json +1 -1
  28. package/dist/template/scripts/build-search-index.mjs +120 -25
  29. package/dist/template/src/app/api/chat/route.ts +11 -3
  30. package/dist/template/src/app/docs/README.md +28 -0
  31. package/dist/template/src/app/docs/[...slug]/page.tsx +141 -14
  32. package/dist/template/src/app/docs/auth/page.mdx +589 -0
  33. package/dist/template/src/app/docs/autofix/page.mdx +624 -0
  34. package/dist/template/src/app/docs/cli/page.mdx +217 -0
  35. package/dist/template/src/app/docs/config/page.mdx +428 -0
  36. package/dist/template/src/app/docs/configuration/page.mdx +86 -0
  37. package/dist/template/src/app/docs/deployment/page.mdx +112 -0
  38. package/dist/template/src/app/docs/error.tsx +20 -0
  39. package/dist/template/src/app/docs/generator/generator.md +504 -0
  40. package/dist/template/src/app/docs/generator/organizer.md +779 -0
  41. package/dist/template/src/app/docs/generator/page.mdx +613 -0
  42. package/dist/template/src/app/docs/github/page.mdx +502 -0
  43. package/dist/template/src/app/docs/llm/anthropic-client.md +549 -0
  44. package/dist/template/src/app/docs/llm/index.md +471 -0
  45. package/dist/template/src/app/docs/llm/page.mdx +428 -0
  46. package/dist/template/src/app/docs/llms-full.md +256 -0
  47. package/dist/template/src/app/docs/llms.txt +2971 -0
  48. package/dist/template/src/app/docs/not-found.tsx +23 -0
  49. package/dist/template/src/app/docs/page.mdx +0 -3
  50. package/dist/template/src/app/docs/plugins/page.mdx +1793 -0
  51. package/dist/template/src/app/docs/pro/page.mdx +121 -0
  52. package/dist/template/src/app/docs/quickstart/page.mdx +93 -0
  53. package/dist/template/src/app/docs/scanner/content-type.md +599 -0
  54. package/dist/template/src/app/docs/scanner/index.md +212 -0
  55. package/dist/template/src/app/docs/scanner/page.mdx +307 -0
  56. package/dist/template/src/app/docs/scanner/python.md +469 -0
  57. package/dist/template/src/app/docs/scanner/python_parser.md +1056 -0
  58. package/dist/template/src/app/docs/scanner/rust.md +325 -0
  59. package/dist/template/src/app/docs/scanner/typescript.md +201 -0
  60. package/dist/template/src/app/error.tsx +3 -3
  61. package/dist/template/src/app/icon.tsx +29 -0
  62. package/dist/template/src/app/layout.tsx +57 -7
  63. package/dist/template/src/app/not-found.tsx +35 -0
  64. package/dist/template/src/app/page.tsx +95 -11
  65. package/dist/template/src/components/ai-chat.tsx +26 -21
  66. package/dist/template/src/components/breadcrumbs.tsx +56 -12
  67. package/dist/template/src/components/copy-button.tsx +17 -3
  68. package/dist/template/src/components/docs-layout.tsx +202 -8
  69. package/dist/template/src/components/feedback.tsx +4 -2
  70. package/dist/template/src/components/footer.tsx +42 -0
  71. package/dist/template/src/components/header.tsx +56 -20
  72. package/dist/template/src/components/mdx/accordion.tsx +17 -13
  73. package/dist/template/src/components/mdx/callout.tsx +50 -37
  74. package/dist/template/src/components/mdx/card.tsx +24 -12
  75. package/dist/template/src/components/mdx/code-block.tsx +17 -3
  76. package/dist/template/src/components/mdx/code-group.tsx +78 -18
  77. package/dist/template/src/components/mdx/code-playground.tsx +3 -0
  78. package/dist/template/src/components/mdx/go-playground.tsx +3 -0
  79. package/dist/template/src/components/mdx/highlighted-code.tsx +178 -38
  80. package/dist/template/src/components/mdx/python-playground.tsx +2 -0
  81. package/dist/template/src/components/mdx/steps.tsx +6 -6
  82. package/dist/template/src/components/mdx/tabs.tsx +76 -8
  83. package/dist/template/src/components/page-header.tsx +19 -0
  84. package/dist/template/src/components/scroll-to-top.tsx +33 -0
  85. package/dist/template/src/components/search-dialog.tsx +251 -57
  86. package/dist/template/src/components/sidebar.tsx +137 -77
  87. package/dist/template/src/components/table-of-contents.tsx +29 -13
  88. package/dist/template/src/lib/highlight.ts +90 -31
  89. package/dist/template/src/lib/search.ts +14 -4
  90. package/dist/template/src/lib/theme-utils.ts +140 -0
  91. package/dist/template/src/styles/globals.css +397 -84
  92. package/dist/template/src/types/remark-gfm.d.ts +2 -0
  93. package/dist/utils/files.d.ts +9 -0
  94. package/dist/utils/files.js +33 -0
  95. package/dist/utils/validation.d.ts +4 -0
  96. package/dist/utils/validation.js +38 -0
  97. package/package.json +1 -4
@@ -1,43 +1,237 @@
1
1
  'use client'
2
2
 
3
- import { useState } from 'react'
3
+ import { useState, useEffect } from 'react'
4
+ import { usePathname } from 'next/navigation'
5
+ import Link from 'next/link'
6
+ import { ChevronLeft, ChevronRight, ChevronDown } 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'
11
+ import { PageHeader } from './page-header'
12
+ import { Feedback } from './feedback'
13
+ import { EditLink } from './edit-link'
14
+ import { Footer } from './footer'
15
+ import { ScrollToTop } from './scroll-to-top'
8
16
 
9
17
  interface DocsConfig {
18
+ name?: string
19
+ headerLinks?: Array<{ title: string; path: string }>
20
+ logo?: string
10
21
  navigation: Array<{
11
22
  group: string
12
- pages: Array<{ title: string; path: string }>
23
+ icon?: string
24
+ pages: Array<{ title: string; path: string; description?: string }>
13
25
  }>
26
+ footer?: {
27
+ links?: Array<{ title: string; url: string }>
28
+ }
29
+ editLink?: {
30
+ repoUrl?: string
31
+ branch?: string
32
+ docsPath?: string
33
+ }
14
34
  }
15
35
 
16
- export function DocsLayout({ children, docsConfig }: { children: React.ReactNode; docsConfig: DocsConfig }) {
36
+ function getAllPages(config: DocsConfig): Array<{ title: string; path: string }> {
37
+ return config.navigation.flatMap((group) => group.pages)
38
+ }
39
+
40
+ function PrevNextNav({ docsConfig }: { docsConfig: DocsConfig }) {
41
+ const pathname = usePathname()
42
+ const allPages = getAllPages(docsConfig)
43
+ const currentIndex = allPages.findIndex((p) => p.path === pathname)
44
+
45
+ if (currentIndex === -1) return null
46
+
47
+ const prev = currentIndex > 0 ? allPages[currentIndex - 1] : null
48
+ const next = currentIndex < allPages.length - 1 ? allPages[currentIndex + 1] : null
49
+
50
+ if (!prev && !next) return null
51
+
52
+ return (
53
+ <div className="mt-12 pt-6 border-t border-[var(--color-border)] grid grid-cols-2 gap-4">
54
+ {prev ? (
55
+ <Link
56
+ href={prev.path}
57
+ 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"
58
+ >
59
+ <span className="flex items-center gap-1 text-xs text-[var(--color-text-tertiary)]">
60
+ <ChevronLeft size={12} />
61
+ Previous
62
+ </span>
63
+ <span className="text-sm font-medium text-[var(--color-text)] group-hover:text-[var(--color-primary)]">
64
+ {prev.title}
65
+ </span>
66
+ </Link>
67
+ ) : (
68
+ <div />
69
+ )}
70
+ {next && (
71
+ <Link
72
+ href={next.path}
73
+ 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"
74
+ >
75
+ <span className="flex items-center gap-1 text-xs text-[var(--color-text-tertiary)]">
76
+ Next
77
+ <ChevronRight size={12} />
78
+ </span>
79
+ <span className="text-sm font-medium text-[var(--color-text)] group-hover:text-[var(--color-primary)]">
80
+ {next.title}
81
+ </span>
82
+ </Link>
83
+ )}
84
+ </div>
85
+ )
86
+ }
87
+
88
+ /** Mobile TOC — collapsible "On this page" for screens < xl */
89
+ function MobileTOC() {
90
+ const [headings, setHeadings] = useState<Array<{ id: string; text: string; level: number }>>([])
91
+ const [open, setOpen] = useState(false)
92
+ const pathname = usePathname()
93
+
94
+ useEffect(() => {
95
+ const article = document.querySelector('article')
96
+ if (!article) return
97
+
98
+ const elements = article.querySelectorAll('h2, h3')
99
+ const items: Array<{ id: string; text: string; level: number }> = []
100
+
101
+ const genericHeadings = new Set([
102
+ 'parameters', 'returns', 'return value', 'return type',
103
+ 'returned validator function', 'requirements',
104
+ 'when results are returned', 'when each value is returned',
105
+ 'validationresult shape',
106
+ ])
107
+
108
+ elements.forEach((el) => {
109
+ const text = el.textContent || ''
110
+ const normalized = text.toLowerCase().trim()
111
+ if (genericHeadings.has(normalized)) return
112
+
113
+ const id = el.id || normalized.replace(/\s+/g, '-')
114
+ if (!el.id) el.id = id
115
+ items.push({ id, text, level: parseInt(el.tagName[1]) })
116
+ })
117
+
118
+ setHeadings(items)
119
+ setOpen(false)
120
+ }, [pathname])
121
+
122
+ if (headings.length === 0) return null
123
+
124
+ return (
125
+ <div className="lg:hidden mb-6 border border-[var(--color-border)] rounded-lg">
126
+ <button
127
+ onClick={() => setOpen(!open)}
128
+ className="flex items-center justify-between w-full px-4 py-3 text-[0.8125rem] font-medium text-[var(--color-text)]"
129
+ aria-expanded={open}
130
+ >
131
+ On this page
132
+ <ChevronDown size={14} className={`text-[var(--color-text-tertiary)] transition-transform duration-150 ${open ? 'rotate-180' : ''}`} />
133
+ </button>
134
+ {open && (
135
+ <div className="px-4 pb-3 border-t border-[var(--color-border)]">
136
+ <ul className="space-y-1 mt-2">
137
+ {headings.map((heading, i) => (
138
+ <li key={`${heading.id}-${i}`}>
139
+ <a
140
+ href={`#${heading.id}`}
141
+ onClick={() => setOpen(false)}
142
+ className={`block py-1 text-[0.8125rem] text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] hover:no-underline ${
143
+ heading.level === 3 ? 'pl-4' : ''
144
+ }`}
145
+ >
146
+ {heading.text}
147
+ </a>
148
+ </li>
149
+ ))}
150
+ </ul>
151
+ </div>
152
+ )}
153
+ </div>
154
+ )
155
+ }
156
+
157
+ function getPageInfo(docsConfig: DocsConfig, pathname: string): { title?: string; description?: string } {
158
+ for (const group of docsConfig.navigation) {
159
+ for (const page of group.pages) {
160
+ if (page.path === pathname) {
161
+ return { title: page.title, description: page.description }
162
+ }
163
+ }
164
+ }
165
+ return {}
166
+ }
167
+
168
+ export function DocsLayout({
169
+ children,
170
+ docsConfig,
171
+ pageTitle,
172
+ pageDescription,
173
+ }: {
174
+ children: React.ReactNode
175
+ docsConfig: DocsConfig
176
+ pageTitle?: string
177
+ pageDescription?: string
178
+ }) {
17
179
  const [menuOpen, setMenuOpen] = useState(false)
180
+ const [frontmatterDescription, setFrontmatterDescription] = useState<string | undefined>()
181
+ const pathname = usePathname()
182
+
183
+ // Read frontmatter description passed from the server component via data attribute
184
+ useEffect(() => {
185
+ const el = document.querySelector('[data-page-description]')
186
+ setFrontmatterDescription(el?.getAttribute('data-page-description') ?? undefined)
187
+ }, [pathname])
188
+
189
+ // Resolve title from props, falling back to docs.json navigation lookup
190
+ const pageInfo = getPageInfo(docsConfig, pathname)
191
+ const resolvedTitle = pageTitle ?? pageInfo.title
192
+ const resolvedDescription = pageDescription ?? frontmatterDescription ?? pageInfo.description
18
193
 
19
194
  return (
20
- <div className="min-h-screen">
195
+ <div className="min-h-screen flex flex-col">
21
196
  <Header
22
197
  onMenuToggle={() => setMenuOpen(!menuOpen)}
23
198
  menuOpen={menuOpen}
199
+ siteName={docsConfig.name}
200
+ navLinks={docsConfig.headerLinks}
201
+ logo={docsConfig.logo}
24
202
  />
25
- <div className="flex">
203
+ <div className="flex flex-1">
26
204
  <Sidebar
27
205
  open={menuOpen}
28
206
  onClose={() => setMenuOpen(false)}
29
207
  docsConfig={docsConfig}
30
208
  />
31
- <main className="flex-1 min-w-0 px-4 md:px-8 py-6 md:ml-[var(--sidebar-width)] xl:mr-[var(--toc-width)]">
32
- <div className="max-w-3xl mx-auto">
33
- <Breadcrumbs />
209
+ <main id="main-content" className="flex-1 min-w-0 px-6 md:px-10 py-8 lg:ml-[var(--sidebar-width)] lg:mr-[var(--toc-width)]">
210
+ <div className="max-w-[var(--content-max-width)] mx-auto">
211
+ <Breadcrumbs docsConfig={docsConfig} />
212
+ {resolvedTitle && (
213
+ <PageHeader title={resolvedTitle} description={resolvedDescription} />
214
+ )}
215
+ <MobileTOC />
34
216
  <article className="prose">
35
217
  {children}
36
218
  </article>
219
+ {/* Feedback and Edit Link */}
220
+ <div className="mt-10 pt-6 border-t border-[var(--color-border)] flex items-center justify-between flex-wrap gap-4">
221
+ <Feedback />
222
+ <EditLink
223
+ repoUrl={docsConfig.editLink?.repoUrl}
224
+ branch={docsConfig.editLink?.branch}
225
+ docsPath={docsConfig.editLink?.docsPath}
226
+ />
227
+ </div>
228
+ <PrevNextNav docsConfig={docsConfig} />
37
229
  </div>
38
230
  </main>
39
231
  <TableOfContents />
40
232
  </div>
233
+ <Footer docsConfig={docsConfig} />
234
+ <ScrollToTop />
41
235
  </div>
42
236
  )
43
237
  }
@@ -28,9 +28,10 @@ export function Feedback() {
28
28
  <div className="flex items-center gap-1">
29
29
  <button
30
30
  onClick={() => handleFeedback(true)}
31
+ aria-label="Helpful"
31
32
  className={`p-1.5 rounded-md transition-colors ${
32
33
  selection === 'yes'
33
- ? 'bg-emerald-100 text-emerald-600'
34
+ ? 'bg-emerald-100 text-emerald-600 feedback-positive'
34
35
  : 'text-[var(--color-text-tertiary)] hover:text-[var(--color-text)] hover:bg-[var(--color-bg-secondary)]'
35
36
  }`}
36
37
  >
@@ -38,9 +39,10 @@ export function Feedback() {
38
39
  </button>
39
40
  <button
40
41
  onClick={() => handleFeedback(false)}
42
+ aria-label="Not helpful"
41
43
  className={`p-1.5 rounded-md transition-colors ${
42
44
  selection === 'no'
43
- ? 'bg-red-100 text-red-600'
45
+ ? 'bg-red-100 text-red-600 feedback-negative'
44
46
  : 'text-[var(--color-text-tertiary)] hover:text-[var(--color-text)] hover:bg-[var(--color-bg-secondary)]'
45
47
  }`}
46
48
  >
@@ -0,0 +1,42 @@
1
+ interface FooterProps {
2
+ docsConfig: {
3
+ footer?: {
4
+ links?: Array<{ title: string; url: string }>
5
+ }
6
+ }
7
+ }
8
+
9
+ export function Footer({ docsConfig }: FooterProps) {
10
+ const links = docsConfig.footer?.links || []
11
+
12
+ return (
13
+ <footer className="border-t border-[var(--color-border)] lg:ml-[var(--sidebar-width)]">
14
+ <div className="max-w-[var(--content-max-width)] mx-auto px-6 md:px-10 py-6 flex items-center justify-between flex-wrap gap-4">
15
+ <div className="flex items-center gap-6">
16
+ {links.map((link) => (
17
+ <a
18
+ key={link.url}
19
+ href={link.url}
20
+ target="_blank"
21
+ rel="noopener noreferrer"
22
+ className="text-[0.8125rem] text-[var(--color-text-tertiary)] hover:text-[var(--color-text)] transition-colors"
23
+ >
24
+ {link.title}
25
+ </a>
26
+ ))}
27
+ </div>
28
+ <span className="text-[0.75rem] text-[var(--color-text-tertiary)]">
29
+ Built with{' '}
30
+ <a
31
+ href="https://skrypt.sh"
32
+ target="_blank"
33
+ rel="noopener noreferrer"
34
+ className="text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors"
35
+ >
36
+ Skrypt
37
+ </a>
38
+ </span>
39
+ </div>
40
+ </footer>
41
+ )
42
+ }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import Link from 'next/link'
4
4
  import { Search, Menu, X } from 'lucide-react'
5
- import { useState } from 'react'
5
+ import { useState, useEffect } from 'react'
6
6
  import { SearchDialog } from './search-dialog'
7
7
  import { ThemeToggle } from './theme-toggle'
8
8
  import { SyntaxThemeSelector } from './syntax-theme-selector'
@@ -10,48 +10,84 @@ 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 }>
15
+ logo?: string
13
16
  }
14
17
 
15
- export function Header({ onMenuToggle, menuOpen }: HeaderProps) {
18
+ export function Header({ onMenuToggle, menuOpen, siteName = 'Docs', navLinks, logo }: HeaderProps) {
16
19
  const [searchOpen, setSearchOpen] = useState(false)
20
+ const [isMac, setIsMac] = useState(true)
21
+
22
+ useEffect(() => {
23
+ setIsMac(
24
+ navigator.platform?.toLowerCase().includes('mac') ||
25
+ navigator.userAgent?.toLowerCase().includes('mac')
26
+ )
27
+ }, [])
28
+
29
+ useEffect(() => {
30
+ function handleKeyDown(e: KeyboardEvent) {
31
+ if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
32
+ e.preventDefault()
33
+ setSearchOpen(prev => !prev)
34
+ }
35
+ }
36
+ document.addEventListener('keydown', handleKeyDown)
37
+ return () => document.removeEventListener('keydown', handleKeyDown)
38
+ }, [])
17
39
 
18
40
  return (
19
41
  <>
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">
42
+ <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">
43
+ <div className="flex items-center justify-between h-full px-4 md:px-6">
44
+ <div className="flex items-center gap-6">
23
45
  {/* Mobile menu button */}
24
46
  <button
25
47
  onClick={onMenuToggle}
26
- className="md:hidden p-2 -ml-2 text-[var(--color-text-secondary)] hover:text-[var(--color-text)]"
48
+ 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
49
  aria-label={menuOpen ? 'Close menu' : 'Open menu'}
28
50
  aria-expanded={menuOpen}
29
51
  >
30
- {menuOpen ? <X size={20} /> : <Menu size={20} />}
52
+ {menuOpen ? <X size={18} /> : <Menu size={18} />}
31
53
  </button>
32
54
 
33
- <Link href="/" className="font-semibold text-lg">
34
- Docs
55
+ <Link href="/" className="flex items-center gap-2 font-semibold text-[0.9375rem] tracking-tight text-[var(--color-text)] hover:no-underline">
56
+ {logo ? (
57
+ <img src={logo} alt={siteName} className="h-6 w-auto" />
58
+ ) : (
59
+ siteName
60
+ )}
35
61
  </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)]">
62
+
63
+ <nav className="hidden md:flex items-center gap-1">
64
+ <Link
65
+ href="/docs"
66
+ 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"
67
+ >
38
68
  Documentation
39
69
  </Link>
40
- <Link href="/docs/api" className="text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)]">
41
- API Reference
42
- </Link>
70
+ {navLinks?.map((link) => (
71
+ <Link
72
+ key={link.path}
73
+ href={link.path}
74
+ 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"
75
+ >
76
+ {link.title}
77
+ </Link>
78
+ ))}
43
79
  </nav>
44
80
  </div>
45
81
 
46
- <div className="flex items-center gap-2">
82
+ <div className="flex items-center gap-1.5">
47
83
  <button
48
84
  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"
85
+ 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
86
  >
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">
54
- ⌘K
87
+ <Search size={14} />
88
+ <span className="hidden sm:inline">Search...</span>
89
+ <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">
90
+ {isMac ? '⌘K' : 'Ctrl+K'}
55
91
  </kbd>
56
92
  </button>
57
93
  <SyntaxThemeSelector />
@@ -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,31 @@ 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
+ aria-expanded={open}
21
+ 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
22
  >
22
- {title}
23
- <ChevronDown
24
- size={20}
23
+ <ChevronRight
24
+ size={14}
25
25
  className={cn(
26
- 'text-[var(--color-text-tertiary)] transition-transform',
27
- open && 'rotate-180'
26
+ 'text-[var(--color-text-tertiary)] transition-transform duration-200 shrink-0',
27
+ open && 'rotate-90'
28
28
  )}
29
29
  />
30
+ {title}
30
31
  </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)]">
32
+ <div
33
+ className="grid transition-all duration-200"
34
+ style={{ gridTemplateRows: open ? '1fr' : '0fr' }}
35
+ >
36
+ <div className="overflow-hidden">
37
+ <div className="pl-5 pb-4 text-[0.8125rem] text-[var(--color-text-secondary)] leading-relaxed">
34
38
  {children}
35
39
  </div>
36
40
  </div>
37
- )}
41
+ </div>
38
42
  </div>
39
43
  )
40
44
  }
@@ -44,5 +48,5 @@ interface AccordionGroupProps {
44
48
  }
45
49
 
46
50
  export function AccordionGroup({ children }: AccordionGroupProps) {
47
- return <div className="my-6 space-y-2">{children}</div>
51
+ return <div className="my-6 border-t border-[var(--color-border)]">{children}</div>
48
52
  }
@@ -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
  }