Skip to content

Commit cda168e

Browse files
authored
Merge pull request #109 from nativeui-org/feat/add-open-in-llms-on-docs
feat(component-preview): enhance component navigation and documentation features
2 parents 4b72b3f + 60c4b74 commit cda168e

38 files changed

+19092
-6
lines changed

components/docs/component-preview.tsx

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
import React, { useState, useEffect } from "react";
44
import { CodeBlock } from "@/components/ui/code-block";
55
import { InstallationTabs } from "@/components/docs/installation-tabs";
6+
import { Button } from "@/components/ui/button";
7+
import {
8+
DropdownMenu,
9+
DropdownMenuContent,
10+
DropdownMenuItem,
11+
DropdownMenuTrigger,
12+
} from "@/components/ui/dropdown-menu";
613

714
export interface ComponentExample {
815
title: string;
@@ -102,6 +109,48 @@ export function ComponentPreview({
102109
dependencies: false,
103110
});
104111

112+
// Fonctions helper pour LLM et navigation
113+
const copyPageContent = async () => {
114+
const content = `# ${name}\n\n${description}\n\n## Installation\n\n\`\`\`bash\nnpx @nativeui/cli add ${registryName}\n\`\`\`\n\n## Code\n\n\`\`\`tsx\n${componentCode}\n\`\`\`\n\n## Usage\n\n\`\`\`tsx\n${previewCode}\n\`\`\``;
115+
await navigator.clipboard.writeText(content);
116+
};
117+
118+
const openInLLM = (llm: string) => {
119+
const prompt = `I'm looking at this NativeUI documentation: https://nativeui.io/docs/components/${registryName}.md
120+
Help me understand how to use it. Be ready to explain concepts, give examples, or help debug based on it.`;
121+
122+
const urls = {
123+
chatgpt: `https://chat.openai.com/?q=${encodeURIComponent(prompt)}`,
124+
claude: `https://claude.ai/new?q=${encodeURIComponent(prompt)}`
125+
};
126+
127+
window.open(urls[llm as keyof typeof urls], '_blank');
128+
};
129+
130+
const viewAsMarkdown = () => {
131+
window.open(`/docs/components/${registryName}.md`, '_blank');
132+
};
133+
134+
const navigateToComponent = (direction: 'next' | 'prev') => {
135+
// Liste des composants (à adapter selon votre structure)
136+
const components = ['accordion', 'alert', 'alert-dialog', 'avatar', 'badge', 'breadcrumb', 'button', 'calendar', 'card', 'carousel', 'checkbox', 'collapsible', 'combobox', 'date-time-picker', 'dialog', 'drawer', 'dropdown', 'input', 'input-otp', 'label', 'pagination', 'popover', 'progress', 'radio-group', 'select', 'separator', 'sheet', 'skeleton', 'slider', 'switch', 'table', 'tabs', 'textarea', 'toggle', 'toggle-group', 'tooltip'];
137+
138+
const currentIndex = components.indexOf(registryName);
139+
if (currentIndex === -1) return;
140+
141+
let targetIndex;
142+
if (direction === 'next') {
143+
targetIndex = currentIndex + 1;
144+
if (targetIndex >= components.length) targetIndex = 0;
145+
} else {
146+
targetIndex = currentIndex - 1;
147+
if (targetIndex < 0) targetIndex = components.length - 1;
148+
}
149+
150+
const targetComponent = components[targetIndex];
151+
window.location.href = `/docs/components/${targetComponent}`;
152+
};
153+
105154
useEffect(() => {
106155
const loadAllResources = async () => {
107156
setIsLoading(true);
@@ -162,7 +211,67 @@ export function ComponentPreview({
162211
return (
163212
<div className="container max-w-3xl py-10">
164213
<div className="space-y-4">
165-
<h1 className="text-3xl font-bold tracking-tight">{name}</h1>
214+
<div className="flex items-center justify-between">
215+
<h1 className="text-3xl font-bold tracking-tight">{name}</h1>
216+
<div className="flex items-center gap-2">
217+
<Button
218+
variant="ghost"
219+
size="sm"
220+
onClick={() => navigateToComponent('prev')}
221+
className="text-muted-foreground hover:text-foreground"
222+
>
223+
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
224+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
225+
</svg>
226+
</Button>
227+
<Button
228+
variant="ghost"
229+
size="sm"
230+
onClick={() => navigateToComponent('next')}
231+
className="text-muted-foreground hover:text-foreground"
232+
>
233+
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
234+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
235+
</svg>
236+
</Button>
237+
<DropdownMenu>
238+
<DropdownMenuTrigger asChild>
239+
<Button variant="outline" size="sm" className="gap-2">
240+
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
241+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
242+
</svg>
243+
Copy Page
244+
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
245+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
246+
</svg>
247+
</Button>
248+
</DropdownMenuTrigger>
249+
<DropdownMenuContent align="end" className="w-48">
250+
<DropdownMenuItem onClick={copyPageContent}>
251+
<svg className="h-4 w-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
252+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
253+
</svg>
254+
Copy as Markdown
255+
</DropdownMenuItem>
256+
<DropdownMenuItem onClick={viewAsMarkdown}>
257+
<svg className="h-4 w-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
258+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
259+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
260+
</svg>
261+
View as Markdown
262+
</DropdownMenuItem>
263+
<DropdownMenuItem onClick={() => openInLLM('chatgpt')}>
264+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M22.282 9.821a5.985 5.985 0 0 0-.516-4.91 6.046 6.046 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a5.985 5.985 0 0 0-3.998 2.9 6.046 6.046 0 0 0 .743 7.097 5.98 5.98 0 0 0 .51 4.911 6.051 6.051 0 0 0 6.515 2.9A5.985 5.985 0 0 0 13.26 24a6.056 6.056 0 0 0 5.772-4.206 5.99 5.99 0 0 0 3.997-2.9 6.056 6.056 0 0 0-.747-7.073zM13.26 22.43a4.476 4.476 0 0 1-2.876-1.04l.141-.081 4.779-2.758a.795.795 0 0 0 .392-.681v-6.737l2.02 1.168a.071.071 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494zM3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085 4.783 2.759a.771.771 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646zM2.34 7.896a4.485 4.485 0 0 1 2.366-1.973V11.6a.766.766 0 0 0 .388.676l5.815 3.355-2.02 1.168a.076.076 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855-5.833-3.387L15.119 7.2a.076.076 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667zm2.01-3.023-.141-.085-4.774-2.782a.776.776 0 0 0-.785 0L9.409 9.23V6.897a.066.066 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08-4.778 2.758a.795.795 0 0 0-.393.681zm1.097-2.365 2.602-1.5 2.607 1.5v2.999l-2.597 1.5-2.607-1.5Z" fill="currentColor"></path></svg>
265+
Open in ChatGPT
266+
</DropdownMenuItem>
267+
<DropdownMenuItem onClick={() => openInLLM('claude')}>
268+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m4.714 15.956 4.718-2.648.079-.23-.08-.128h-.23l-.79-.048-2.695-.073-2.337-.097-2.265-.122-.57-.121-.535-.704.055-.353.48-.321.685.06 1.518.104 2.277.157 1.651.098 2.447.255h.389l.054-.158-.133-.097-.103-.098-2.356-1.596-2.55-1.688-1.336-.972-.722-.491L2 6.223l-.158-1.008.655-.722.88.06.225.061.893.686 1.906 1.476 2.49 1.833.364.304.146-.104.018-.072-.164-.274-1.354-2.446-1.445-2.49-.644-1.032-.17-.619a2.972 2.972 0 0 1-.103-.729L6.287.133 6.7 0l.995.134.42.364.619 1.415L9.735 4.14l1.555 3.03.455.898.243.832.09.255h.159V9.01l.127-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.583.28.48.685-.067.444-.286 1.851-.558 2.903-.365 1.942h.213l.243-.242.983-1.306 1.652-2.064.728-.82.85-.904.547-.431h1.032l.759 1.129-.34 1.166-1.063 1.347-.88 1.142-1.263 1.7-.79 1.36.074.11.188-.02 2.853-.606 1.542-.28 1.84-.315.832.388.09.395-.327.807-1.967.486-2.307.462-3.436.813-.043.03.049.061 1.548.146.662.036h1.62l3.018.225.79.522.473.638-.08.485-1.213.62-1.64-.389-3.825-.91-1.31-.329h-.183v.11l1.093 1.068 2.003 1.81 2.508 2.33.127.578-.321.455-.34-.049-2.204-1.657-.85-.747-1.925-1.62h-.127v.17l.443.649 2.343 3.521.122 1.08-.17.353-.607.213-.668-.122-1.372-1.924-1.415-2.168-1.141-1.943-.14.08-.674 7.254-.316.37-.728.28-.607-.461-.322-.747.322-1.476.388-1.924.316-1.53.285-1.9.17-.632-.012-.042-.14.018-1.432 1.967-2.18 2.945-1.724 1.845-.413.164-.716-.37.066-.662.401-.589 2.386-3.036 1.439-1.882.929-1.086-.006-.158h-.055L4.138 18.56l-1.13.146-.485-.456.06-.746.231-.243 1.907-1.312Z" fill="currentColor"></path></svg>
269+
Open in Claude
270+
</DropdownMenuItem>
271+
</DropdownMenuContent>
272+
</DropdownMenu>
273+
</div>
274+
</div>
166275
<p className="text-muted-foreground text-lg">{description}</p>
167276
</div>
168277

@@ -279,17 +388,17 @@ export function ComponentPreview({
279388
<button
280389
onClick={() => setActiveInstallTab("cli")}
281390
className={`px-4 py-2 text-sm font-medium ${activeInstallTab === "cli"
282-
? "border-b-2 border-primary text-primary"
283-
: "text-muted-foreground hover:text-foreground"
391+
? "border-b-2 border-primary text-primary"
392+
: "text-muted-foreground hover:text-foreground"
284393
}`}
285394
>
286395
CLI
287396
</button>
288397
<button
289398
onClick={() => setActiveInstallTab("manual")}
290399
className={`px-4 py-2 text-sm font-medium ${activeInstallTab === "manual"
291-
? "border-b-2 border-primary text-primary"
292-
: "text-muted-foreground hover:text-foreground"
400+
? "border-b-2 border-primary text-primary"
401+
: "text-muted-foreground hover:text-foreground"
293402
}`}
294403
>
295404
Manual

0 commit comments

Comments
 (0)