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.
- package/README.md +1 -1
- package/dist/auth/index.d.ts +0 -1
- package/dist/auth/index.js +3 -5
- package/dist/autofix/index.js +15 -3
- package/dist/cli.js +19 -4
- package/dist/commands/check-links.js +164 -174
- package/dist/commands/deploy.js +5 -2
- package/dist/commands/generate.js +206 -199
- package/dist/commands/i18n.js +3 -20
- package/dist/commands/init.js +47 -40
- package/dist/commands/lint.js +3 -20
- package/dist/commands/mcp.js +125 -122
- package/dist/commands/monitor.js +125 -108
- package/dist/commands/review-pr.js +1 -1
- package/dist/commands/sdk.js +1 -1
- package/dist/config/loader.js +21 -2
- package/dist/generator/organizer.d.ts +3 -0
- package/dist/generator/organizer.js +4 -9
- package/dist/generator/writer.js +2 -10
- package/dist/github/pr-comments.js +21 -8
- package/dist/plugins/index.js +1 -0
- package/dist/scanner/index.js +8 -2
- package/dist/template/docs.json +2 -1
- package/dist/template/next.config.mjs +3 -1
- package/dist/template/package.json +17 -14
- package/dist/template/public/favicon.svg +4 -0
- package/dist/template/public/search-index.json +1 -1
- package/dist/template/scripts/build-search-index.mjs +120 -25
- package/dist/template/src/app/api/chat/route.ts +11 -3
- package/dist/template/src/app/docs/README.md +28 -0
- package/dist/template/src/app/docs/[...slug]/page.tsx +141 -14
- package/dist/template/src/app/docs/auth/page.mdx +589 -0
- package/dist/template/src/app/docs/autofix/page.mdx +624 -0
- package/dist/template/src/app/docs/cli/page.mdx +217 -0
- package/dist/template/src/app/docs/config/page.mdx +428 -0
- package/dist/template/src/app/docs/configuration/page.mdx +86 -0
- package/dist/template/src/app/docs/deployment/page.mdx +112 -0
- package/dist/template/src/app/docs/error.tsx +20 -0
- package/dist/template/src/app/docs/generator/generator.md +504 -0
- package/dist/template/src/app/docs/generator/organizer.md +779 -0
- package/dist/template/src/app/docs/generator/page.mdx +613 -0
- package/dist/template/src/app/docs/github/page.mdx +502 -0
- package/dist/template/src/app/docs/llm/anthropic-client.md +549 -0
- package/dist/template/src/app/docs/llm/index.md +471 -0
- package/dist/template/src/app/docs/llm/page.mdx +428 -0
- package/dist/template/src/app/docs/llms-full.md +256 -0
- package/dist/template/src/app/docs/llms.txt +2971 -0
- package/dist/template/src/app/docs/not-found.tsx +23 -0
- package/dist/template/src/app/docs/page.mdx +0 -3
- package/dist/template/src/app/docs/plugins/page.mdx +1793 -0
- package/dist/template/src/app/docs/pro/page.mdx +121 -0
- package/dist/template/src/app/docs/quickstart/page.mdx +93 -0
- package/dist/template/src/app/docs/scanner/content-type.md +599 -0
- package/dist/template/src/app/docs/scanner/index.md +212 -0
- package/dist/template/src/app/docs/scanner/page.mdx +307 -0
- package/dist/template/src/app/docs/scanner/python.md +469 -0
- package/dist/template/src/app/docs/scanner/python_parser.md +1056 -0
- package/dist/template/src/app/docs/scanner/rust.md +325 -0
- package/dist/template/src/app/docs/scanner/typescript.md +201 -0
- package/dist/template/src/app/error.tsx +3 -3
- package/dist/template/src/app/icon.tsx +29 -0
- package/dist/template/src/app/layout.tsx +57 -7
- package/dist/template/src/app/not-found.tsx +35 -0
- package/dist/template/src/app/page.tsx +95 -11
- package/dist/template/src/components/ai-chat.tsx +26 -21
- package/dist/template/src/components/breadcrumbs.tsx +56 -12
- package/dist/template/src/components/copy-button.tsx +17 -3
- package/dist/template/src/components/docs-layout.tsx +202 -8
- package/dist/template/src/components/feedback.tsx +4 -2
- package/dist/template/src/components/footer.tsx +42 -0
- package/dist/template/src/components/header.tsx +56 -20
- package/dist/template/src/components/mdx/accordion.tsx +17 -13
- package/dist/template/src/components/mdx/callout.tsx +50 -37
- package/dist/template/src/components/mdx/card.tsx +24 -12
- package/dist/template/src/components/mdx/code-block.tsx +17 -3
- package/dist/template/src/components/mdx/code-group.tsx +78 -18
- package/dist/template/src/components/mdx/code-playground.tsx +3 -0
- package/dist/template/src/components/mdx/go-playground.tsx +3 -0
- package/dist/template/src/components/mdx/highlighted-code.tsx +178 -38
- package/dist/template/src/components/mdx/python-playground.tsx +2 -0
- package/dist/template/src/components/mdx/steps.tsx +6 -6
- package/dist/template/src/components/mdx/tabs.tsx +76 -8
- package/dist/template/src/components/page-header.tsx +19 -0
- package/dist/template/src/components/scroll-to-top.tsx +33 -0
- package/dist/template/src/components/search-dialog.tsx +251 -57
- package/dist/template/src/components/sidebar.tsx +137 -77
- package/dist/template/src/components/table-of-contents.tsx +29 -13
- package/dist/template/src/lib/highlight.ts +90 -31
- package/dist/template/src/lib/search.ts +14 -4
- package/dist/template/src/lib/theme-utils.ts +140 -0
- package/dist/template/src/styles/globals.css +397 -84
- package/dist/template/src/types/remark-gfm.d.ts +2 -0
- package/dist/utils/files.d.ts +9 -0
- package/dist/utils/files.js +33 -0
- package/dist/utils/validation.d.ts +4 -0
- package/dist/utils/validation.js +38 -0
- package/package.json +1 -4
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import Link from 'next/link'
|
|
2
|
+
|
|
3
|
+
export default function NotFound() {
|
|
4
|
+
return (
|
|
5
|
+
<main className="min-h-screen flex flex-col items-center justify-center px-6">
|
|
6
|
+
<div className="text-center max-w-md">
|
|
7
|
+
<p className="text-[5rem] font-bold text-[var(--color-primary)] leading-none mb-4">404</p>
|
|
8
|
+
<h1 className="text-2xl font-bold text-[var(--color-text)] mb-3">
|
|
9
|
+
Page not found
|
|
10
|
+
</h1>
|
|
11
|
+
<p className="text-[var(--color-text-secondary)] mb-8 leading-relaxed">
|
|
12
|
+
The page you are looking for does not exist or may have been moved.
|
|
13
|
+
Try searching the documentation or return to the homepage.
|
|
14
|
+
</p>
|
|
15
|
+
<div className="flex items-center justify-center gap-3">
|
|
16
|
+
<Link
|
|
17
|
+
href="/docs"
|
|
18
|
+
className="inline-flex items-center gap-2 px-5 py-2.5 bg-[var(--color-primary)] text-[var(--color-primary-foreground)] rounded-lg font-medium text-[0.875rem] hover:opacity-90 hover:no-underline transition-opacity"
|
|
19
|
+
>
|
|
20
|
+
Go to Docs
|
|
21
|
+
</Link>
|
|
22
|
+
<Link
|
|
23
|
+
href="/"
|
|
24
|
+
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"
|
|
25
|
+
>
|
|
26
|
+
Homepage
|
|
27
|
+
</Link>
|
|
28
|
+
</div>
|
|
29
|
+
<p className="mt-8 text-[0.8125rem] text-[var(--color-text-tertiary)]">
|
|
30
|
+
Tip: Use <kbd className="px-1.5 py-0.5 text-[0.75rem] bg-[var(--color-bg-secondary)] border border-[var(--color-border)] rounded">Ctrl+K</kbd> to search the documentation.
|
|
31
|
+
</p>
|
|
32
|
+
</div>
|
|
33
|
+
</main>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -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,102 @@ 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'
|
|
15
|
+
|
|
16
|
+
// Collect first 6 pages for quick links
|
|
17
|
+
const allPages: Array<{ title: string; path: string }> = []
|
|
18
|
+
for (const group of docsConfig.navigation || []) {
|
|
19
|
+
for (const page of group.pages || []) {
|
|
20
|
+
if (allPages.length < 6) allPages.push(page)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
13
23
|
|
|
14
24
|
return (
|
|
15
|
-
<main className="min-h-screen flex flex-col
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
<main id="main-content" className="min-h-screen flex flex-col">
|
|
26
|
+
{/* Header */}
|
|
27
|
+
<header className="h-[var(--header-height)] border-b border-[var(--color-border)] flex items-center px-6">
|
|
28
|
+
<Link href="/" className="font-semibold text-[0.9375rem] tracking-tight text-[var(--color-text)] hover:no-underline">
|
|
29
|
+
{docsConfig.name}
|
|
30
|
+
</Link>
|
|
31
|
+
</header>
|
|
32
|
+
|
|
33
|
+
{/* Hero */}
|
|
34
|
+
<div className="flex-1 flex flex-col items-center justify-center px-6 py-20">
|
|
35
|
+
<div className="max-w-2xl mx-auto text-center relative">
|
|
36
|
+
{/* Gradient glow behind title */}
|
|
37
|
+
<div
|
|
38
|
+
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[400px] h-[200px] rounded-full blur-[100px] opacity-20 pointer-events-none"
|
|
39
|
+
style={{ background: `radial-gradient(ellipse, var(--color-primary), transparent 70%)` }}
|
|
40
|
+
/>
|
|
41
|
+
|
|
42
|
+
<h1 className="relative text-4xl sm:text-5xl font-bold tracking-tight text-[var(--color-text)] mb-4">
|
|
43
|
+
{docsConfig.name}
|
|
44
|
+
</h1>
|
|
45
|
+
<p className="relative text-lg text-[var(--color-text-secondary)] mb-8 max-w-lg mx-auto leading-relaxed">
|
|
46
|
+
{docsConfig.description}
|
|
47
|
+
</p>
|
|
48
|
+
<div className="relative flex items-center justify-center gap-3">
|
|
49
|
+
<Link
|
|
50
|
+
href={firstPage}
|
|
51
|
+
className="inline-flex items-center gap-2 px-5 py-2.5 bg-[var(--color-primary)] text-[var(--color-primary-foreground)] rounded-lg font-medium text-[0.875rem] hover:opacity-90 hover:no-underline transition-opacity"
|
|
52
|
+
>
|
|
53
|
+
Get Started
|
|
54
|
+
<ArrowRight size={16} />
|
|
55
|
+
</Link>
|
|
56
|
+
<Link
|
|
57
|
+
href="/docs"
|
|
58
|
+
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"
|
|
59
|
+
>
|
|
60
|
+
Documentation
|
|
61
|
+
</Link>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
{/* Feature cards — show first page title + description from each group */}
|
|
66
|
+
<div className="flex flex-wrap justify-center gap-4 max-w-2xl mx-auto mt-16 w-full">
|
|
67
|
+
{(docsConfig.navigation || []).slice(0, 3).map((group: { group: string; pages: Array<{ title: string; path: string }> }, i: number) => {
|
|
68
|
+
const icons = [BookOpen, Code, Zap]
|
|
69
|
+
const Icon = icons[i % icons.length]
|
|
70
|
+
const firstGroupPage = group.pages?.[0]
|
|
71
|
+
return (
|
|
72
|
+
<Link
|
|
73
|
+
key={group.group}
|
|
74
|
+
href={firstGroupPage?.path || '/docs'}
|
|
75
|
+
className="block p-5 rounded-xl border border-[var(--color-border)] bg-[var(--color-bg)] hover:border-[var(--color-primary)] transition-colors hover:no-underline w-full sm:w-[calc(50%-0.5rem)] min-w-[200px]"
|
|
76
|
+
>
|
|
77
|
+
<Icon size={18} className="text-[var(--color-primary)] mb-3" />
|
|
78
|
+
<h3 className="text-[0.875rem] font-semibold text-[var(--color-text)] mb-1">{group.group}</h3>
|
|
79
|
+
<p className="text-[0.8125rem] text-[var(--color-text-tertiary)] leading-relaxed">
|
|
80
|
+
{firstGroupPage ? `Start with ${firstGroupPage.title}` : `${group.pages.length} pages`}
|
|
81
|
+
</p>
|
|
82
|
+
<p className="text-[0.75rem] text-[var(--color-text-tertiary)] mt-1">
|
|
83
|
+
{group.pages.length} {group.pages.length === 1 ? 'page' : 'pages'}
|
|
84
|
+
</p>
|
|
85
|
+
</Link>
|
|
86
|
+
)
|
|
87
|
+
})}
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
{/* Quick Links */}
|
|
91
|
+
{allPages.length > 0 && (
|
|
92
|
+
<div className="max-w-2xl mx-auto mt-10 w-full">
|
|
93
|
+
<p className="text-[0.6875rem] font-semibold uppercase tracking-widest text-[var(--color-text-tertiary)] mb-3 text-center">
|
|
94
|
+
Quick Links
|
|
95
|
+
</p>
|
|
96
|
+
<div className="flex flex-wrap justify-center gap-x-6 gap-y-2">
|
|
97
|
+
{allPages.map((page) => (
|
|
98
|
+
<Link
|
|
99
|
+
key={page.path}
|
|
100
|
+
href={page.path}
|
|
101
|
+
className="text-[0.8125rem] text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] hover:no-underline transition-colors"
|
|
102
|
+
>
|
|
103
|
+
{page.title}
|
|
104
|
+
</Link>
|
|
105
|
+
))}
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
)}
|
|
109
|
+
</div>
|
|
26
110
|
</main>
|
|
27
111
|
)
|
|
28
112
|
}
|
|
@@ -4,6 +4,7 @@ import { useState, useRef, useEffect } from 'react'
|
|
|
4
4
|
import { MessageSquare, X, Send, Loader2, ExternalLink } from 'lucide-react'
|
|
5
5
|
|
|
6
6
|
interface Message {
|
|
7
|
+
id: string
|
|
7
8
|
role: 'user' | 'assistant'
|
|
8
9
|
content: string
|
|
9
10
|
citations?: Array<{ title: string; path: string; snippet: string }>
|
|
@@ -36,7 +37,7 @@ export function AIChat({
|
|
|
36
37
|
|
|
37
38
|
const userMessage = input.trim()
|
|
38
39
|
setInput('')
|
|
39
|
-
setMessages(prev => [...prev, { role: 'user', content: userMessage }])
|
|
40
|
+
setMessages(prev => [...prev, { id: Date.now().toString(), role: 'user', content: userMessage }])
|
|
40
41
|
setIsLoading(true)
|
|
41
42
|
|
|
42
43
|
try {
|
|
@@ -56,6 +57,7 @@ export function AIChat({
|
|
|
56
57
|
setMessages(prev => [
|
|
57
58
|
...prev,
|
|
58
59
|
{
|
|
60
|
+
id: Date.now().toString(),
|
|
59
61
|
role: 'assistant',
|
|
60
62
|
content: data.content,
|
|
61
63
|
citations: data.citations,
|
|
@@ -65,6 +67,7 @@ export function AIChat({
|
|
|
65
67
|
setMessages(prev => [
|
|
66
68
|
...prev,
|
|
67
69
|
{
|
|
70
|
+
id: Date.now().toString(),
|
|
68
71
|
role: 'assistant',
|
|
69
72
|
content: 'Sorry, I encountered an error. Please try again.',
|
|
70
73
|
},
|
|
@@ -78,7 +81,7 @@ export function AIChat({
|
|
|
78
81
|
return (
|
|
79
82
|
<button
|
|
80
83
|
onClick={() => setIsOpen(true)}
|
|
81
|
-
className="fixed bottom-6 right-6 z-50 flex h-14 w-14 items-center justify-center rounded-full bg-
|
|
84
|
+
className="fixed bottom-6 right-6 z-50 flex h-14 w-14 items-center justify-center rounded-full bg-[var(--color-text)] text-[var(--color-bg)] shadow-lg transition-transform hover:scale-105"
|
|
82
85
|
aria-label="Open AI chat"
|
|
83
86
|
>
|
|
84
87
|
<MessageSquare className="h-6 w-6" />
|
|
@@ -87,21 +90,22 @@ export function AIChat({
|
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
return (
|
|
90
|
-
<div className="fixed bottom-6 right-6 z-50 flex h-[500px] w-[380px] flex-col overflow-hidden rounded-2xl border border-
|
|
93
|
+
<div className="fixed bottom-6 right-6 z-50 flex h-[500px] w-[380px] flex-col overflow-hidden rounded-2xl border border-[var(--color-border)] bg-[var(--color-bg)] shadow-2xl">
|
|
91
94
|
{/* Header */}
|
|
92
|
-
<div className="flex items-center justify-between border-b border-
|
|
95
|
+
<div className="flex items-center justify-between border-b border-[var(--color-border)] px-4 py-3">
|
|
93
96
|
<div className="flex items-center gap-2">
|
|
94
|
-
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-
|
|
95
|
-
<MessageSquare className="h-4 w-4 text-
|
|
97
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-[var(--color-text)]">
|
|
98
|
+
<MessageSquare className="h-4 w-4 text-[var(--color-bg)]" />
|
|
96
99
|
</div>
|
|
97
100
|
<div>
|
|
98
|
-
<p className="text-sm font-medium text-
|
|
99
|
-
<p className="text-xs text-
|
|
101
|
+
<p className="text-sm font-medium text-[var(--color-text)]">Ask AI</p>
|
|
102
|
+
<p className="text-xs text-[var(--color-text-tertiary)]">About {projectName}</p>
|
|
100
103
|
</div>
|
|
101
104
|
</div>
|
|
102
105
|
<button
|
|
103
106
|
onClick={() => setIsOpen(false)}
|
|
104
|
-
className="rounded-lg p-1 text-
|
|
107
|
+
className="rounded-lg p-1 text-[var(--color-text-tertiary)] transition-colors hover:bg-[var(--color-bg-secondary)] hover:text-[var(--color-text-secondary)]"
|
|
108
|
+
aria-label="Close chat"
|
|
105
109
|
>
|
|
106
110
|
<X className="h-5 w-5" />
|
|
107
111
|
</button>
|
|
@@ -110,36 +114,36 @@ export function AIChat({
|
|
|
110
114
|
{/* Messages */}
|
|
111
115
|
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
|
112
116
|
{messages.length === 0 && (
|
|
113
|
-
<div className="text-center text-sm text-
|
|
117
|
+
<div className="text-center text-sm text-[var(--color-text-tertiary)] py-8">
|
|
114
118
|
<p>Ask me anything about {projectName}.</p>
|
|
115
119
|
<p className="mt-2 text-xs">I'll search the docs and give you an answer with sources.</p>
|
|
116
120
|
</div>
|
|
117
121
|
)}
|
|
118
122
|
|
|
119
|
-
{messages.map((message
|
|
123
|
+
{messages.map((message) => (
|
|
120
124
|
<div
|
|
121
|
-
key={
|
|
125
|
+
key={message.id}
|
|
122
126
|
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
|
123
127
|
>
|
|
124
128
|
<div
|
|
125
129
|
className={`max-w-[85%] rounded-2xl px-4 py-2 text-sm ${
|
|
126
130
|
message.role === 'user'
|
|
127
|
-
? 'bg-
|
|
128
|
-
: 'bg-
|
|
131
|
+
? 'bg-[var(--color-text)] text-[var(--color-bg)]'
|
|
132
|
+
: 'bg-[var(--color-bg-tertiary)] text-[var(--color-text)]'
|
|
129
133
|
}`}
|
|
130
134
|
>
|
|
131
135
|
<p className="whitespace-pre-wrap">{message.content}</p>
|
|
132
136
|
|
|
133
137
|
{/* Citations */}
|
|
134
138
|
{message.citations && message.citations.length > 0 && (
|
|
135
|
-
<div className="mt-3 border-t border-
|
|
136
|
-
<p className="text-xs font-medium text-
|
|
139
|
+
<div className="mt-3 border-t border-[var(--color-border)] pt-2">
|
|
140
|
+
<p className="text-xs font-medium text-[var(--color-text-tertiary)] mb-1">Sources:</p>
|
|
137
141
|
<div className="space-y-1">
|
|
138
142
|
{message.citations.map((citation, j) => (
|
|
139
143
|
<a
|
|
140
144
|
key={j}
|
|
141
145
|
href={citation.path}
|
|
142
|
-
className="flex items-center gap-1 text-xs text-
|
|
146
|
+
className="flex items-center gap-1 text-xs text-[var(--color-primary)] hover:underline"
|
|
143
147
|
>
|
|
144
148
|
<ExternalLink className="h-3 w-3" />
|
|
145
149
|
{citation.title}
|
|
@@ -154,7 +158,7 @@ export function AIChat({
|
|
|
154
158
|
|
|
155
159
|
{isLoading && (
|
|
156
160
|
<div className="flex justify-start">
|
|
157
|
-
<div className="flex items-center gap-2 rounded-2xl bg-
|
|
161
|
+
<div className="flex items-center gap-2 rounded-2xl bg-[var(--color-bg-tertiary)] px-4 py-2 text-sm text-[var(--color-text-tertiary)]">
|
|
158
162
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
159
163
|
Searching docs...
|
|
160
164
|
</div>
|
|
@@ -167,7 +171,7 @@ export function AIChat({
|
|
|
167
171
|
{/* Input */}
|
|
168
172
|
<form
|
|
169
173
|
onSubmit={handleSubmit}
|
|
170
|
-
className="border-t border-
|
|
174
|
+
className="border-t border-[var(--color-border)] p-3"
|
|
171
175
|
>
|
|
172
176
|
<div className="flex items-center gap-2">
|
|
173
177
|
<input
|
|
@@ -176,12 +180,13 @@ export function AIChat({
|
|
|
176
180
|
onChange={e => setInput(e.target.value)}
|
|
177
181
|
placeholder={placeholder}
|
|
178
182
|
disabled={isLoading}
|
|
179
|
-
className="flex-1 rounded-xl border border-
|
|
183
|
+
className="flex-1 rounded-xl border border-[var(--color-border)] bg-[var(--color-bg-secondary)] px-4 py-2 text-sm text-[var(--color-text)] outline-none transition-colors placeholder:text-[var(--color-text-tertiary)] focus:border-[var(--color-border-strong)]"
|
|
180
184
|
/>
|
|
181
185
|
<button
|
|
182
186
|
type="submit"
|
|
183
187
|
disabled={!input.trim() || isLoading}
|
|
184
|
-
className="flex h-10 w-10 items-center justify-center rounded-xl bg-
|
|
188
|
+
className="flex h-10 w-10 items-center justify-center rounded-xl bg-[var(--color-text)] text-[var(--color-bg)] transition-colors hover:opacity-90 disabled:opacity-50"
|
|
189
|
+
aria-label="Send message"
|
|
185
190
|
>
|
|
186
191
|
<Send className="h-4 w-4" />
|
|
187
192
|
</button>
|
|
@@ -2,17 +2,61 @@
|
|
|
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
|
+
interface NavPage { title: string; path: string; pages?: NavPage[] }
|
|
8
|
+
interface NavGroup { group: string; pages: (NavPage | NavGroup)[] }
|
|
9
|
+
interface DocsConfig { navigation: NavGroup[] }
|
|
10
|
+
|
|
11
|
+
function isNavGroup(item: NavPage | NavGroup): item is NavGroup {
|
|
12
|
+
return 'group' in item && !('path' in item)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function buildTitleMap(docsConfig?: DocsConfig): Map<string, string> {
|
|
16
|
+
const map = new Map<string, string>()
|
|
17
|
+
if (!docsConfig) return map
|
|
18
|
+
|
|
19
|
+
for (const group of docsConfig.navigation) {
|
|
20
|
+
for (const item of group.pages) {
|
|
21
|
+
if (isNavGroup(item)) {
|
|
22
|
+
fillTitleMap(item.pages, map)
|
|
23
|
+
} else {
|
|
24
|
+
map.set(item.path, item.title)
|
|
25
|
+
if (item.pages) fillTitleMap(item.pages, map)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return map
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function fillTitleMap(pages: (NavPage | NavGroup)[], map: Map<string, string>) {
|
|
33
|
+
for (const item of pages) {
|
|
34
|
+
if (isNavGroup(item)) {
|
|
35
|
+
fillTitleMap(item.pages, map)
|
|
36
|
+
} else {
|
|
37
|
+
map.set(item.path, item.title)
|
|
38
|
+
if (item.pages) fillTitleMap(item.pages, map)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface BreadcrumbsProps {
|
|
44
|
+
docsConfig?: DocsConfig
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function Breadcrumbs({ docsConfig }: BreadcrumbsProps) {
|
|
8
48
|
const pathname = usePathname()
|
|
9
49
|
const segments = pathname.split('/').filter(Boolean)
|
|
10
50
|
|
|
11
51
|
if (segments.length <= 1) return null
|
|
12
52
|
|
|
53
|
+
const titleMap = buildTitleMap(docsConfig)
|
|
54
|
+
|
|
13
55
|
const crumbs = segments.map((segment, index) => {
|
|
14
56
|
const href = '/' + segments.slice(0, index + 1).join('/')
|
|
15
|
-
|
|
57
|
+
// Look up the actual page title from navigation config
|
|
58
|
+
const configTitle = titleMap.get(href)
|
|
59
|
+
const label = configTitle || segment
|
|
16
60
|
.replace(/-/g, ' ')
|
|
17
61
|
.replace(/\b\w/g, (c) => c.toUpperCase())
|
|
18
62
|
|
|
@@ -20,17 +64,17 @@ export function Breadcrumbs() {
|
|
|
20
64
|
})
|
|
21
65
|
|
|
22
66
|
return (
|
|
23
|
-
<nav className="flex items-center gap-1
|
|
24
|
-
<Link href="/" className="hover:text-[var(--color-text)]">
|
|
25
|
-
|
|
67
|
+
<nav className="flex items-center gap-1 text-[0.8125rem] text-[var(--color-text-tertiary)] mb-6">
|
|
68
|
+
<Link href="/" className="hover:text-[var(--color-text)] hover:no-underline">
|
|
69
|
+
Docs
|
|
26
70
|
</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>
|
|
71
|
+
{crumbs.slice(1).map((crumb, i) => (
|
|
72
|
+
<span key={crumb.href} className="flex items-center gap-1">
|
|
73
|
+
<ChevronRight size={12} className="text-[var(--color-text-tertiary)]" />
|
|
74
|
+
{i === crumbs.length - 2 ? (
|
|
75
|
+
<span className="text-[var(--color-text-secondary)]">{crumb.label}</span>
|
|
32
76
|
) : (
|
|
33
|
-
<Link href={crumb.href} className="hover:text-[var(--color-text)]">
|
|
77
|
+
<Link href={crumb.href} className="hover:text-[var(--color-text)] hover:no-underline">
|
|
34
78
|
{crumb.label}
|
|
35
79
|
</Link>
|
|
36
80
|
)}
|
|
@@ -12,9 +12,23 @@ export function CopyButton({ text, className = '' }: CopyButtonProps) {
|
|
|
12
12
|
const [copied, setCopied] = useState(false)
|
|
13
13
|
|
|
14
14
|
async function handleCopy() {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
try {
|
|
16
|
+
await navigator.clipboard.writeText(text)
|
|
17
|
+
setCopied(true)
|
|
18
|
+
setTimeout(() => setCopied(false), 2000)
|
|
19
|
+
} catch {
|
|
20
|
+
// Fallback for insecure contexts
|
|
21
|
+
const textarea = document.createElement('textarea')
|
|
22
|
+
textarea.value = text
|
|
23
|
+
textarea.style.position = 'fixed'
|
|
24
|
+
textarea.style.opacity = '0'
|
|
25
|
+
document.body.appendChild(textarea)
|
|
26
|
+
textarea.select()
|
|
27
|
+
document.execCommand('copy')
|
|
28
|
+
document.body.removeChild(textarea)
|
|
29
|
+
setCopied(true)
|
|
30
|
+
setTimeout(() => setCopied(false), 2000)
|
|
31
|
+
}
|
|
18
32
|
}
|
|
19
33
|
|
|
20
34
|
return (
|