Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions components/copy-page.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* CopyPage Component Styles */

.container {
position: relative;
margin-right: 16px;
margin-left: 10px;
display: flex;
}

.mainButton {
display: flex;
align-items: center;
gap: 2px;
padding: 6px 10px;
font-size: 12px;
font-weight: 500;
color: #374151;
background-color: white;
border: 1px solid #d1d5db;
border-right: none;
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
cursor: pointer;
transition: all 0.2s;
}

.mainButton:hover {
background-color: #f9fafb;
}

.arrowButton {
display: flex;
align-items: center;
justify-content: center;
padding: 6px 6px;
font-size: 12px;
font-weight: 500;
color: #374151;
background-color: white;
border: 1px solid #d1d5db;
border-left: 1px solid #d1d5db;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
cursor: pointer;
transition: all 0.2s;
}

.arrowButton:hover {
background-color: #f9fafb;
}

.arrowIcon {
width: 16px;
height: 16px;
transition: transform 0.2s;
}

.arrowIconRotated {
transform: rotate(180deg);
}

.dropdown {
position: absolute;
right: 0;
top: 100%;
margin-top: 8px;
width: 256px;
background-color: white;
border: 1px solid #e5e7eb;
border-radius: 6px;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
z-index: 50;
}

.dropdownContent {
padding: 4px 0;
}

.dropdownButton {
display: flex;
align-items: center;
width: 100%;
padding: 12px 16px;
font-size: 14px;
color: #374151;
background-color: transparent;
border: none;
cursor: pointer;
text-align: left;
transition: background-color 0.2s;
}

.dropdownButton:hover {
background-color: #f3f4f6;
}

.mainButtonIcon {
width: 16px;
height: 16px;
margin-right: 6px;
}

.dropdownIcon {
width: 16px;
height: 16px;
margin-right: 12px;
}

.dropdownText {
display: flex;
flex-direction: column;
align-items: flex-start;
flex: 1;
}

.dropdownTitle {
font-weight: 500;
}

.dropdownDescription {
font-size: 12px;
color: #6b7280;
margin-top: 2px;
}

@media (max-width: 640px) {
.container {
display: none;
}
}
156 changes: 156 additions & 0 deletions components/copy-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import React, { useState, useRef, useEffect } from 'react';
import { useRouter } from 'next/router';
import styles from './copy-page.module.css';
import CopyIcon from './icons/copy';
import CheckIcon from './icons/check';
import ChevronDownIcon from './icons/chevron-down';
import DocumentIcon from './icons/document';
import ChatGPTIcon from './icons/chatgpt';
import AnthropicIcon from './icons/anthropic';

const CopyPage: React.FC = () => {
const [isOpen, setIsOpen] = useState(false);
const [isCopied, setIsCopied] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);

// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};

document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);

const copyPageAsMarkdown = async () => {
try {
// Get the current page content
const pageContent = document.querySelector('main')?.innerText || '';
const pageTitle = document.title;

// Create markdown content
const markdownContent = `# ${pageTitle}\n\n${pageContent}`;

await navigator.clipboard.writeText(markdownContent);

// Show success feedback
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 2000);
} catch (error) {
console.error('Failed to copy page:', error);
}
setIsOpen(false);
};

const viewAsMarkdown = () => {
const pageContent = document.querySelector('main')?.innerText || '';
const pageTitle = document.title;
const markdownContent = `# ${pageTitle}\n\n${pageContent}`;

// Open in new window/tab
const blob = new Blob([markdownContent], { type: 'text/markdown' });
const url = URL.createObjectURL(blob);
window.open(url, '_blank');
URL.revokeObjectURL(url);
setIsOpen(false);
};

const openInAI = (platform: 'chatgpt' | 'claude') => {
const currentUrl = window.location.href;
const prompt = `I'm building with GenLayer - can you read this docs page ${currentUrl} so I can ask you questions about it?`;
const encodedPrompt = encodeURIComponent(prompt);

const urls = {
chatgpt: `https://chatgpt.com/?q=${encodedPrompt}`,
claude: `https://claude.ai/new?q=${encodedPrompt}`
};

window.open(urls[platform], '_blank');
setIsOpen(false);
};

const openInChatGPT = () => openInAI('chatgpt');
const openInClaude = () => openInAI('claude');

return (
<div className={styles.container} ref={dropdownRef}>
<button
onClick={copyPageAsMarkdown}
className={styles.mainButton}
>
{isCopied ? (
<CheckIcon className={styles.mainButtonIcon} />
) : (
<CopyIcon className={styles.mainButtonIcon} />
)}
Copy page
</button>

<button
onClick={() => setIsOpen(!isOpen)}
className={styles.arrowButton}
>
<ChevronDownIcon
className={`${styles.arrowIcon} ${isOpen ? styles.arrowIconRotated : ''}`}
/>
</button>

{isOpen && (
<div className={styles.dropdown}>
<div className={styles.dropdownContent}>
<button
onClick={copyPageAsMarkdown}
className={styles.dropdownButton}
>
<CopyIcon className={styles.dropdownIcon} />
<div className={styles.dropdownText}>
<span className={styles.dropdownTitle}>Copy page</span>
<span className={styles.dropdownDescription}>Copy the page as Markdown for LLMs</span>
</div>
</button>

<button
onClick={viewAsMarkdown}
className={styles.dropdownButton}
>
<DocumentIcon className={styles.dropdownIcon} />
<div className={styles.dropdownText}>
<span className={styles.dropdownTitle}>View as MarkDown</span>
<span className={styles.dropdownDescription}>View this page as plain text</span>
</div>
</button>

<button
onClick={openInChatGPT}
className={styles.dropdownButton}
>
<ChatGPTIcon className={styles.dropdownIcon} />
<div className={styles.dropdownText}>
<span className={styles.dropdownTitle}>Open in ChatGPT</span>
<span className={styles.dropdownDescription}>Ask questions about this page</span>
</div>
</button>

<button
onClick={openInClaude}
className={styles.dropdownButton}
>
<AnthropicIcon className={styles.dropdownIcon} />
<div className={styles.dropdownText}>
<span className={styles.dropdownTitle}>Open in Claude</span>
<span className={styles.dropdownDescription}>Ask questions about this page</span>
</div>
</button>
</div>
</div>
)}
</div>
);
};

export default CopyPage;
7 changes: 7 additions & 0 deletions components/icons/anthropic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function AnthropicIcon({ className }) {
return (
<svg className={className} width="16" height="16" fill="currentColor" viewBox="0 0 92.2 65">
<path d="M66.5,0H52.4l25.7,65h14.1L66.5,0z M25.7,0L0,65h14.4l5.3-13.6h26.9L51.8,65h14.4L40.5,0C40.5,0,25.7,0,25.7,0z M24.3,39.3l8.8-22.8l8.8,22.8H24.3z"/>
</svg>
);
}
7 changes: 7 additions & 0 deletions components/icons/chatgpt.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions components/icons/check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function CheckIcon({ className }) {
return (
<svg className={className} width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
);
}
7 changes: 7 additions & 0 deletions components/icons/chevron-down.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function ChevronDownIcon({ className }) {
return (
<svg className={className} width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
);
}
7 changes: 7 additions & 0 deletions components/icons/copy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function CopyIcon({ className }) {
return (
<svg className={className} width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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" />
</svg>
);
}
File renamed without changes.
7 changes: 7 additions & 0 deletions components/icons/document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function DocumentIcon({ className }) {
return (
<svg className={className} width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
</svg>
);
}
7 changes: 7 additions & 0 deletions components/icons/github.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
File renamed without changes.
16 changes: 10 additions & 6 deletions theme.config.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import React from "react";
import { DocsThemeConfig } from "nextra-theme-docs";
import { useRouter } from "next/router";
import TelegramIcon from "./components/telegram";
import TelegramIcon from "./components/icons/telegram";
import Logo from "./components/icon";
import TwitterLogo from "./components/twitter";
import DiscordIcon from "./components/discord";
import TwitterLogo from "./components/icons/twitter";
import DiscordIcon from "./components/icons/discord";
import GitHubIcon from "./components/icons/github";
import CopyPage from "./components/copy-page";

const config: DocsThemeConfig = {
logo: <Logo />,
project: {
link: "https://github.com/genlayerlabs",
},
docsRepositoryBase: "https://github.com/genlayerlabs/genlayer-docs/tree/main",
footer: {
text: "GenLayer Documentation",
Expand All @@ -27,6 +26,10 @@ const config: DocsThemeConfig = {
navbar: {
extraContent: (
<div style={{ display: "flex", alignItems: "center" }}>
<CopyPage />
<a href="https://github.com/genlayerlabs" style={{ marginRight: 10 }}>
<GitHubIcon />
</a>
<a href="https://t.me/genlayer" style={{ marginRight: 10 }}>
<TelegramIcon />
</a>
Expand All @@ -39,6 +42,7 @@ const config: DocsThemeConfig = {
</div>
),
},

useNextSeoProps: () => {
const { asPath } = useRouter();
const isHomePage = asPath === "/";
Expand Down
Loading