Skip to content

Commit 064381d

Browse files
committed
feat(docs): grouped sidebar nav with icons, per-page Copy-for-LLM (ChatGPT/Claude/Perplexity/Grok/Gemini), mermaid support
1 parent 699f02b commit 064381d

11 files changed

Lines changed: 337 additions & 34 deletions

File tree

docs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ next-env.d.ts
66

77
# auto-generated by prebuild (scripts/gen-llms-full.mjs)
88
public/llms-full.txt
9+
public/md/

docs/app/components/copy-page.jsx

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
'use client'
2+
3+
import { useEffect, useRef, useState } from 'react'
4+
import { usePathname } from 'next/navigation'
5+
6+
const BASE = process.env.NEXT_PUBLIC_BASE_PATH ?? ''
7+
8+
function slugFrom(pathname) {
9+
const seg = (pathname || '/').replace(/\/+$/, '').split('/').filter(Boolean)
10+
return seg.length ? seg[seg.length - 1] : 'index'
11+
}
12+
13+
const Ico = ({ d, size = 14 }) => (
14+
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }} aria-hidden>
15+
<path d={d} />
16+
</svg>
17+
)
18+
const I = {
19+
copy: 'M9 9h10a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2zM5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1',
20+
check: 'M20 6 9 17l-5-5',
21+
md: 'M3 5h18v14H3zM7 15V9l3 3 3-3v6M17 9v4m0 0-2-2m2 2 2-2',
22+
chevron: 'm6 9 6 6 6-6',
23+
ext: 'M15 3h6v6M10 14 21 3M21 14v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5',
24+
chat: 'M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z',
25+
}
26+
27+
const AIS = [
28+
{ name: 'ChatGPT', url: 'https://chatgpt.com/?q=', domain: 'chatgpt.com' },
29+
{ name: 'Claude', url: 'https://claude.ai/new?q=', domain: 'claude.ai' },
30+
{ name: 'Perplexity', url: 'https://www.perplexity.ai/search?q=', domain: 'perplexity.ai' },
31+
{ name: 'Grok', url: 'https://grok.com/?q=', domain: 'grok.com' },
32+
{ name: 'Gemini', url: 'https://gemini.google.com/app?q=', domain: 'gemini.google.com' },
33+
]
34+
const favicon = (domain) => `https://www.google.com/s2/favicons?domain=${domain}&sz=64`
35+
36+
const BORDER = 'light-dark(rgba(0,0,0,0.12), rgba(255,255,255,0.14))'
37+
const HOVER = 'light-dark(rgba(0,0,0,0.05), rgba(255,255,255,0.07))'
38+
const MENU_BG = 'light-dark(#ffffff, #1c1c1f)'
39+
const MUTED = 'light-dark(#52525b, #a1a1aa)'
40+
41+
export default function CopyPage() {
42+
const pathname = usePathname()
43+
const [open, setOpen] = useState(false)
44+
const [copied, setCopied] = useState(false)
45+
const ref = useRef(null)
46+
47+
useEffect(() => {
48+
const onDoc = (e) => ref.current && !ref.current.contains(e.target) && setOpen(false)
49+
document.addEventListener('click', onDoc)
50+
return () => document.removeEventListener('click', onDoc)
51+
}, [])
52+
53+
const slug = slugFrom(pathname)
54+
const mdUrl = `${BASE}/md/${slug}.md`
55+
const pageUrl = typeof window !== 'undefined' ? window.location.href : ''
56+
const absMd = typeof window !== 'undefined' ? `${window.location.origin}${mdUrl}` : mdUrl
57+
const prompt = `Read ${pageUrl} (raw markdown: ${absMd}) — a Zaileys documentation page — then help me with it.`
58+
59+
const copy = async () => {
60+
try {
61+
const text = await (await fetch(mdUrl)).text()
62+
await navigator.clipboard.writeText(text)
63+
} catch {
64+
await navigator.clipboard.writeText(pageUrl)
65+
}
66+
setCopied(true)
67+
setTimeout(() => setCopied(false), 1800)
68+
setOpen(false)
69+
}
70+
71+
const item = {
72+
display: 'flex',
73+
alignItems: 'center',
74+
gap: '0.6rem',
75+
width: '100%',
76+
padding: '0.5rem 0.65rem',
77+
fontSize: '0.82rem',
78+
lineHeight: 1.2,
79+
background: 'transparent',
80+
border: 'none',
81+
color: 'inherit',
82+
cursor: 'pointer',
83+
textDecoration: 'none',
84+
textAlign: 'left',
85+
borderRadius: 7,
86+
whiteSpace: 'nowrap',
87+
}
88+
const hov = (e, on) => (e.currentTarget.style.background = on ? HOVER : 'transparent')
89+
90+
return (
91+
<div ref={ref} style={{ position: 'relative', display: 'inline-flex', colorScheme: 'light dark' }}>
92+
<div
93+
style={{
94+
display: 'inline-flex',
95+
alignItems: 'stretch',
96+
border: `1px solid ${BORDER}`,
97+
borderRadius: 8,
98+
overflow: 'hidden',
99+
fontSize: '0.78rem',
100+
fontWeight: 500,
101+
}}
102+
>
103+
<button
104+
onClick={copy}
105+
title="Copy this page as Markdown for LLMs"
106+
style={{ display: 'inline-flex', alignItems: 'center', gap: '0.4rem', padding: '0.34rem 0.65rem', background: 'transparent', border: 'none', color: 'inherit', cursor: 'pointer', transition: 'background 0.12s' }}
107+
onMouseEnter={(e) => hov(e, true)}
108+
onMouseLeave={(e) => hov(e, false)}
109+
>
110+
<Ico d={copied ? I.check : I.copy} size={13} />
111+
{copied ? 'Copied!' : 'Copy page'}
112+
</button>
113+
<button
114+
onClick={() => setOpen((v) => !v)}
115+
aria-label="More options"
116+
style={{ display: 'inline-flex', alignItems: 'center', padding: '0.34rem 0.4rem', background: 'transparent', border: 'none', borderLeft: `1px solid ${BORDER}`, color: MUTED, cursor: 'pointer', transition: 'background 0.12s' }}
117+
onMouseEnter={(e) => hov(e, true)}
118+
onMouseLeave={(e) => hov(e, false)}
119+
>
120+
<Ico d={I.chevron} size={13} />
121+
</button>
122+
</div>
123+
{open && (
124+
<div
125+
style={{
126+
position: 'absolute',
127+
top: 'calc(100% + 7px)',
128+
right: 0,
129+
zIndex: 50,
130+
minWidth: 215,
131+
padding: 5,
132+
background: MENU_BG,
133+
border: `1px solid ${BORDER}`,
134+
borderRadius: 11,
135+
boxShadow: '0 10px 34px rgba(0,0,0,0.18)',
136+
}}
137+
>
138+
<button style={item} onMouseEnter={(e) => hov(e, true)} onMouseLeave={(e) => hov(e, false)} onClick={copy}>
139+
<Ico d={I.copy} /> Copy page as Markdown
140+
</button>
141+
<a style={item} onMouseEnter={(e) => hov(e, true)} onMouseLeave={(e) => hov(e, false)} href={mdUrl} target="_blank" rel="noreferrer">
142+
<Ico d={I.md} /> View as Markdown
143+
<span style={{ marginLeft: 'auto', color: MUTED }}><Ico d={I.ext} size={12} /></span>
144+
</a>
145+
<div style={{ height: 1, background: BORDER, margin: '4px 6px' }} />
146+
<div style={{ padding: '0.3rem 0.65rem 0.2rem', fontSize: '0.68rem', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', color: MUTED }}>Open in</div>
147+
{AIS.map((ai) => (
148+
<a key={ai.name} style={item} onMouseEnter={(e) => hov(e, true)} onMouseLeave={(e) => hov(e, false)} href={`${ai.url}${encodeURIComponent(prompt)}`} target="_blank" rel="noreferrer">
149+
<img src={favicon(ai.domain)} alt="" width={15} height={15} style={{ borderRadius: 3, flexShrink: 0 }} />
150+
{ai.name}
151+
<span style={{ marginLeft: 'auto', color: MUTED }}><Ico d={I.ext} size={12} /></span>
152+
</a>
153+
))}
154+
</div>
155+
)}
156+
</div>
157+
)
158+
}

docs/app/components/doc-h1.jsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
'use client'
2+
3+
import CopyPage from './copy-page'
4+
5+
export default function DocH1(props) {
6+
return (
7+
<>
8+
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: '-0.5rem' }}>
9+
<CopyPage />
10+
</div>
11+
<h1 {...props} />
12+
</>
13+
)
14+
}

docs/content/_meta.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.

docs/content/_meta.tsx

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type { ReactNode } from 'react'
2+
3+
const paths: Record<string, string> = {
4+
home: 'M3 9.5 12 3l9 6.5M5 9.5V21h14V9.5M9 21v-6h6v6',
5+
download: 'M12 3v12m0 0 4-4m-4 4-4-4M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2',
6+
rocket: 'M5 13c-1.5 1.5-2 5-2 5s3.5-.5 5-2m4.5-8.5a8 8 0 0 1 4 4l-5 3-2-2zM15 9a2 2 0 1 0 0-.01M14 4l6 6c0 4-3 7-7 9l-3-3-3-3c2-4 5-7 9-7z',
7+
sliders: 'M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6',
8+
cpu: 'M9 2v2M15 2v2M9 20v2M15 20v2M2 9h2M2 15h2M20 9h2M20 15h2M5 5h14v14H5zM9 9h6v6H9z',
9+
zap: 'M13 2 4 14h7l-1 8 9-12h-7l1-8z',
10+
send: 'm22 2-7 20-4-9-9-4 20-7z',
11+
image: 'M3 5h18v14H3zM3 16l5-5 4 4 3-3 6 6M9 9a1.5 1.5 0 1 0 0-.01',
12+
pointer: 'm9 9 5 12 1.8-5.2L21 14 9 9zM3 3l4 1M5 7 4 3M14 7l3-3M7 14l-3 3',
13+
sparkles: 'M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8zM19 3v4M21 5h-4M5 17v4M7 19H3',
14+
terminal: 'm4 17 6-6-6-6M12 19h8',
15+
megaphone: 'M3 11v2a1 1 0 0 0 1 1h2l9 5V5L6 10H4a1 1 0 0 0-1 1zM19 8a5 5 0 0 1 0 8',
16+
eye: 'M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7zM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z',
17+
users: 'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8M22 21v-2a4 4 0 0 0-3-3.9M16 3.1a4 4 0 0 1 0 7.8',
18+
network: 'M12 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM5 22a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM19 22a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM12 8v4M6.5 16.5 11 12M17.5 16.5 13 12',
19+
rss: 'M4 11a9 9 0 0 1 9 9M4 4a16 16 0 0 1 16 16M5 19a1 1 0 1 0 0-.01',
20+
shield: 'M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10zM9 12l2 2 4-4',
21+
database: 'M12 3c4.4 0 8 1.3 8 3s-3.6 3-8 3-8-1.3-8-3 3.6-3 8-3zM4 6v12c0 1.7 3.6 3 8 3s8-1.3 8-3V6M4 12c0 1.7 3.6 3 8 3s8-1.3 8-3',
22+
alert: 'M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0zM12 9v4M12 17h.01',
23+
server: 'M3 4h18v6H3zM3 14h18v6H3zM7 7h.01M7 17h.01',
24+
help: 'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3M12 17h.01',
25+
book: 'M4 19.5A2.5 2.5 0 0 1 6.5 17H20M4 19.5A2.5 2.5 0 0 0 6.5 22H20V2H6.5A2.5 2.5 0 0 0 4 4.5v15z',
26+
bot: 'M12 8V4M9 2h6M5 8h14v12H5zM9 13v2M15 13v2M2 14h3M19 14h3',
27+
}
28+
29+
function Icon({ d }: { d: string }) {
30+
return (
31+
<svg
32+
width="16"
33+
height="16"
34+
viewBox="0 0 24 24"
35+
fill="none"
36+
stroke="currentColor"
37+
strokeWidth={1.9}
38+
strokeLinecap="round"
39+
strokeLinejoin="round"
40+
style={{ flexShrink: 0 }}
41+
aria-hidden="true"
42+
>
43+
<path d={d} />
44+
</svg>
45+
)
46+
}
47+
48+
function item(icon: string, label: string): { title: ReactNode } {
49+
return {
50+
title: (
51+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: '0.55rem' }}>
52+
<Icon d={paths[icon]} />
53+
{label}
54+
</span>
55+
),
56+
}
57+
}
58+
59+
export default {
60+
'-- getting-started': { type: 'separator', title: 'Getting Started' },
61+
index: item('home', 'Introduction'),
62+
installation: item('download', 'Installation'),
63+
'getting-started': item('rocket', 'Getting Started'),
64+
configuration: item('sliders', 'Configuration'),
65+
66+
'-- core': { type: 'separator', title: 'Core Concepts' },
67+
client: item('cpu', 'Client & Lifecycle'),
68+
events: item('zap', 'Events'),
69+
70+
'-- messaging': { type: 'separator', title: 'Messaging' },
71+
'sending-messages': item('send', 'Sending Messages'),
72+
media: item('image', 'Media Processing'),
73+
interactive: item('pointer', 'Interactive Messages'),
74+
'rich-responses': item('sparkles', 'Rich Responses (AIRich)'),
75+
commands: item('terminal', 'Commands'),
76+
automation: item('megaphone', 'Broadcast & Schedule'),
77+
78+
'-- social': { type: 'separator', title: 'Social & Channels' },
79+
presence: item('eye', 'Presence'),
80+
groups: item('users', 'Groups'),
81+
community: item('network', 'Communities'),
82+
newsletter: item('rss', 'Newsletters (Channels)'),
83+
privacy: item('shield', 'Privacy & Blocking'),
84+
85+
'-- advanced': { type: 'separator', title: 'Advanced' },
86+
storage: item('database', 'Storage Adapters'),
87+
'error-handling': item('alert', 'Error Handling'),
88+
runtimes: item('server', 'Runtime Support'),
89+
90+
'-- reference': { type: 'separator', title: 'Reference' },
91+
troubleshooting: item('help', 'Troubleshooting & FAQ'),
92+
'api-reference': item('book', 'API Reference'),
93+
skill: item('bot', 'AI Skill'),
94+
}

docs/content/client.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,22 @@ The typical happy path is `idle → connecting → qr-pending/pairing-pending
119119
`connectedreconnectingconnectingconnected` (with backoff), and a clean shutdown goes
120120
`connecteddisconnectingdisconnected`.
121121
122+
```mermaid
123+
stateDiagram-v2
124+
[*] --> idle
125+
idle --> connecting: connect()
126+
connecting --> qr_pending: no creds (QR)
127+
connecting --> pairing_pending: no creds (pairing code)
128+
qr_pending --> connected: scanned
129+
pairing_pending --> connected: paired
130+
connecting --> connected: creds restored
131+
connected --> reconnecting: dropped
132+
reconnecting --> connecting: backoff retry
133+
connected --> disconnecting: disconnect()
134+
disconnecting --> disconnected
135+
disconnected --> [*]
136+
```
137+
122138
### `connect()`
123139
124140
```typescript

docs/mdx-components.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { useMDXComponents as getThemeComponents } from 'nextra-theme-docs'
2+
import DocH1 from './app/components/doc-h1'
23

34
export function useMDXComponents(components) {
45
return {
56
...getThemeComponents(),
7+
h1: DocH1,
68
...components,
79
}
810
}

docs/next.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export default withNextra({
1111
output: 'export',
1212
images: { unoptimized: true },
1313
basePath,
14+
env: { NEXT_PUBLIC_BASE_PATH: basePath },
1415
// Static export + basePath: trailingSlash makes the index RSC prefetch payload
1516
// resolve to `${basePath}/index.txt` instead of the 404-ing `${basePath}.txt`.
1617
trailingSlash: true,

docs/package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"start": "next start"
1313
},
1414
"dependencies": {
15+
"@theguild/remark-mermaid": "^0.3.0",
1516
"next": "^15.5.4",
1617
"nextra": "4.2.17",
1718
"nextra-theme-docs": "4.2.17",

0 commit comments

Comments
 (0)