-
Notifications
You must be signed in to change notification settings - Fork 1
Implement Copy Page Dropdown Functionality #239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
ffa93e0
Add copy page dropdown functionality
devin-ai-integration[bot] 16481ee
Fix external tool URLs and markdown memory leak
devin-ai-integration[bot] 187281c
Fix API endpoint error handling and add path validation
devin-ai-integration[bot] fde368f
Fix environment validation to not block Workers build
devin-ai-integration[bot] 63b80e8
Fix linting error: remove unused filePath variable
devin-ai-integration[bot] 4ed8eaf
Merge branch 'devin/1752665854-copy-page-dropdown' of https://git-man…
devin-ai-integration[bot] ec60b10
Merge branch 'main' into devin/1752665854-copy-page-dropdown
e32cd19
Remove unnecessary try-catch wrapper around validateEnv()
devin-ai-integration[bot] f6b985b
remove unnecesssary try catch
d19931c
Merge branch 'devin/1752665854-copy-page-dropdown' of https://github.…
7a90f3b
Fix gray-matter js-yaml security vulnerability
devin-ai-integration[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { NextRequest } from 'next/server'; | ||
import { readFile } from 'fs/promises'; | ||
import { join } from 'path'; | ||
import matter from 'gray-matter'; | ||
|
||
export async function GET(request: NextRequest) { | ||
try { | ||
const { searchParams } = new URL(request.url); | ||
const path = searchParams.get('path'); | ||
|
||
if (!path) { | ||
return new Response('Path parameter required', { status: 400 }); | ||
} | ||
|
||
if (path.includes('..') || path.includes('\\') || path.startsWith('/')) { | ||
return new Response('Invalid path parameter', { status: 400 }); | ||
} | ||
|
||
const basePath = join(process.cwd(), 'content'); | ||
const indexPath = join(basePath, path, 'index.mdx'); | ||
const directPath = join(basePath, `${path}.mdx`); | ||
|
||
let fileContent: string; | ||
|
||
try { | ||
fileContent = await readFile(indexPath, 'utf-8'); | ||
} catch { | ||
try { | ||
fileContent = await readFile(directPath, 'utf-8'); | ||
} catch { | ||
return new Response('Page not found', { status: 404 }); | ||
} | ||
} | ||
|
||
const { content, data } = matter(fileContent); | ||
|
||
return Response.json({ | ||
content, | ||
title: data.title || '', | ||
description: data.description || '', | ||
path | ||
}); | ||
} catch (error) { | ||
console.error('Error reading page content:', error); | ||
return new Response('Failed to read page content', { status: 500 }); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
'use client'; | ||
|
||
import React, { useState } from 'react'; | ||
import { usePathname } from 'next/navigation'; | ||
import { Copy, ExternalLink, FileText, MessageSquare } from 'lucide-react'; | ||
import * as Popover from '@radix-ui/react-popover'; | ||
|
||
interface PageContent { | ||
content: string; | ||
title: string; | ||
description: string; | ||
path: string; | ||
} | ||
|
||
export default function CopyPageDropdown() { | ||
const [isOpen, setIsOpen] = useState(false); | ||
const [isLoading, setIsLoading] = useState(false); | ||
const pathname = usePathname(); | ||
|
||
const formatMarkdownForLLM = (content: PageContent): string => { | ||
return `# ${content.title}\n\n${content.description ? `${content.description}\n\n` : ''}${content.content}`; | ||
}; | ||
|
||
const fetchPageContent = async (): Promise<PageContent | null> => { | ||
try { | ||
setIsLoading(true); | ||
const contentPath = pathname.startsWith('/') ? pathname.slice(1) : pathname; | ||
|
||
const response = await fetch(`/api/page-content?path=${encodeURIComponent(contentPath)}`); | ||
if (!response.ok) throw new Error('Failed to fetch content'); | ||
|
||
return await response.json(); | ||
} catch (error) { | ||
console.error('Error fetching page content:', error); | ||
return null; | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
const handleCopyMarkdown = async () => { | ||
const content = await fetchPageContent(); | ||
if (!content) return; | ||
|
||
const markdownForLLM = formatMarkdownForLLM(content); | ||
|
||
try { | ||
await navigator.clipboard.writeText(markdownForLLM); | ||
} catch (error) { | ||
console.error('Failed to copy to clipboard:', error); | ||
} | ||
setIsOpen(false); | ||
}; | ||
|
||
const handleViewMarkdown = async () => { | ||
const content = await fetchPageContent(); | ||
if (!content) return; | ||
|
||
const markdownForLLM = formatMarkdownForLLM(content); | ||
const blob = new Blob([markdownForLLM], { type: 'text/markdown' }); | ||
const url = URL.createObjectURL(blob); | ||
window.open(url, '_blank'); | ||
setTimeout(() => URL.revokeObjectURL(url), 100); | ||
setIsOpen(false); | ||
}; | ||
|
||
const handleOpenInChatGPT = async () => { | ||
const content = await fetchPageContent(); | ||
if (!content) return; | ||
|
||
const markdownForLLM = formatMarkdownForLLM(content); | ||
const chatGPTUrl = `https://chatgpt.com/?q=${encodeURIComponent(`Please help me understand this documentation:\n\n${markdownForLLM}`)}`; | ||
window.open(chatGPTUrl, '_blank'); | ||
setIsOpen(false); | ||
}; | ||
|
||
const handleOpenInClaude = async () => { | ||
const content = await fetchPageContent(); | ||
if (!content) return; | ||
|
||
const markdownForLLM = formatMarkdownForLLM(content); | ||
const claudeUrl = `https://claude.ai/new?q=${encodeURIComponent(`Please help me understand this documentation:\n\n${markdownForLLM}`)}`; | ||
window.open(claudeUrl, '_blank'); | ||
setIsOpen(false); | ||
}; | ||
|
||
return ( | ||
<Popover.Root open={isOpen} onOpenChange={setIsOpen}> | ||
<Popover.Trigger asChild> | ||
<button | ||
aria-label="Copy page options" | ||
className="flex items-center justify-center transition-all duration-200 hover:scale-110 active:scale-95 transform-origin-center border border-gray-200 dark:border-cyan-900 rounded-md size-10 hover:border-cyan-300 dark:hover:border-cyan-600" | ||
> | ||
<Copy className="size-4 text-cyan-700 dark:text-cyan-500" /> | ||
</button> | ||
</Popover.Trigger> | ||
<Popover.Content | ||
className="w-64 p-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-md shadow-lg z-50" | ||
align="end" | ||
sideOffset={8} | ||
> | ||
<div className="flex flex-col gap-1"> | ||
<button | ||
onClick={handleCopyMarkdown} | ||
disabled={isLoading} | ||
className="flex items-center gap-2 w-full p-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-700 rounded text-left disabled:opacity-50" | ||
> | ||
<Copy className="size-4" /> | ||
Copy as Markdown for LLMs | ||
</button> | ||
<button | ||
onClick={handleViewMarkdown} | ||
disabled={isLoading} | ||
className="flex items-center gap-2 w-full p-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-700 rounded text-left disabled:opacity-50" | ||
> | ||
<FileText className="size-4" /> | ||
View as Markdown | ||
</button> | ||
<button | ||
onClick={handleOpenInChatGPT} | ||
disabled={isLoading} | ||
className="flex items-center gap-2 w-full p-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-700 rounded text-left disabled:opacity-50" | ||
> | ||
<MessageSquare className="size-4" /> | ||
Open in ChatGPT | ||
</button> | ||
<button | ||
onClick={handleOpenInClaude} | ||
disabled={isLoading} | ||
className="flex items-center gap-2 w-full p-2 text-sm hover:bg-gray-100 dark:hover:bg-gray-700 rounded text-left disabled:opacity-50" | ||
> | ||
<ExternalLink className="size-4" /> | ||
Open in Claude | ||
</button> | ||
</div> | ||
</Popover.Content> | ||
</Popover.Root> | ||
); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve user feedback for errors.
Currently, errors are only logged to the console. Users don't receive any feedback when operations fail, which creates a poor user experience.
Consider adding user-visible error states or toast notifications to inform users when operations fail.
🤖 Prompt for AI Agents