Skip to content

docs(copyButton): Add LLM question feature to the document copy button#470

Open
MaxLee-dev wants to merge 8 commits intomainfrom
copy-button-with-llm-links
Open

docs(copyButton): Add LLM question feature to the document copy button#470
MaxLee-dev wants to merge 8 commits intomainfrom
copy-button-with-llm-links

Conversation

@MaxLee-dev
Copy link
Contributor

@MaxLee-dev MaxLee-dev commented Feb 4, 2026

Summary

  • Modified the Markdown copy button to a split button format
  • Added LLM question functionality to the menu (Claude, ChatGPT)
  • Applied external link icon and Korean UI

Changes

  • copy-button.tsx: Refactored into split button + menu structure
  • page.tsx: Added full domain to markdownUrl
  • tool-detail-sheet-client.tsx: Removed size prop

Features

Feature Description
Copy Markdown Copy document to clipboard in Markdown format
View as Markdown Open original Markdown file in new tab
Ask Claude Open Claude chat with document URL
Ask ChatGPT Open ChatGPT chat with document URL

- 마크다운 복사 버튼을 split button 형태로 변경
- 드롭다운 메뉴에 다음 기능 추가:
  - 마크다운으로 보기 (새 탭에서 열기)
  - Claude에게 질문하기
  - ChatGPT에게 질문하기
- Anthropic, OpenAI 아이콘 추가
- 외부 링크 아이콘 표시
- useMemo로 메뉴 아이템 최적화
- 모든 텍스트 한국어로 변경

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link
Contributor

vercel bot commented Feb 4, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vapor-ui Ready Ready Preview, Comment Feb 5, 2026 4:34am

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Feb 4, 2026

⚠️ No Changeset found

Latest commit: bba12f8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @MaxLee-dev, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 문서 복사 버튼의 기능을 확장하여 사용자 편의성을 크게 향상시키는 것을 목표로 합니다. 단순히 마크다운을 복사하는 것을 넘어, 사용자가 문서를 외부 LLM(대규모 언어 모델)에 직접 질문할 수 있는 기능을 통합하여 정보 탐색 및 활용을 더욱 용이하게 합니다. 이는 사용자가 문서 내용을 더 깊이 이해하고 다양한 방식으로 활용할 수 있도록 돕는 중요한 개선 사항입니다.

Highlights

  • 마크다운 복사 버튼 개선: 기존의 단순 복사 버튼이 스플릿 버튼 형태로 변경되어, 복사 기능 외에 추가적인 옵션을 제공합니다.
  • LLM 질문 기능 추가: 드롭다운 메뉴를 통해 현재 보고 있는 문서의 URL을 기반으로 Claude 또는 ChatGPT에 질문할 수 있는 기능이 추가되었습니다.
  • 사용자 인터페이스 현지화 및 개선: 외부 링크 아이콘이 추가되었고, 모든 UI 텍스트가 한국어로 적용되어 사용자 경험이 향상되었습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • apps/website/src/components/copy-button/copy-button.tsx
    • 기존 CopyButton 컴포넌트를 HStackMenu를 활용한 스플릿 버튼 구조로 리팩토링했습니다.
    • Anthropic 및 OpenAI 아이콘을 위한 SVG 컴포넌트를 추가했습니다.
    • 문서 URL을 기반으로 Claude 및 ChatGPT 채팅을 여는 openLLMChat 함수를 구현했습니다.
    • 복사 작업 중 로딩 상태를 관리하기 위한 isLoading 상태를 추가하고, handleCopyContent 함수를 업데이트했습니다.
    • 드롭다운 메뉴 항목으로 '마크다운으로 보기', 'Claude에게 질문하기', 'ChatGPT에게 질문하기'를 추가하고, 각 항목에 해당하는 아이콘과 외부 링크 표시를 적용했습니다.
    • 버튼 및 메뉴의 모든 텍스트를 한국어로 변경했습니다.
Activity
  • MaxLee-dev님이 문서 복사 버튼에 LLM 질문 기능을 추가하는 PR을 생성했습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@MaxLee-dev MaxLee-dev changed the title feat(website): 문서 복사 버튼에 LLM 질문 기능 추가 docs(copyButton): Add LLM question feature to the document copy button Feb 4, 2026
- docs/[[...slug]]/page.tsx: vapor-ui.goorm.io 도메인 추가
- theme/[[...tool]]/page.tsx: vapor-ui.goorm.io 도메인 추가
- blocks/[...slug]/page.tsx: markdownUrl prop 전달
- block-page-header.tsx: markdownUrl prop 추가 및 조건부 렌더링

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이 PR은 문서 복사 버튼에 LLM 질문 기능을 추가하여 사용자 편의성을 높였습니다. 기존 버튼을 분할 버튼(Split Button)으로 리팩토링하고, 드롭다운 메뉴에 Claude 및 ChatGPT 질문 기능을 추가한 점은 긍정적입니다. 그러나 markdownUrl prop 처리와 관련하여 잠재적인 보안 문제가 있습니다. 특히, 이 prop에 대한 유효성 검사 부족은 Cross-Site Scripting (XSS), Prompt Injection 및 안전하지 않은 데이터 가져오기로 이어질 수 있습니다. 이러한 위험을 완화하기 위해 강력한 URL 유효성 검사 및 프로토콜 검사를 추가하는 것이 좋습니다. 전반적으로 좋은 개선이지만, 가독성과 유지보수성을 높이기 위한 몇 가지 제안 사항과 함께 보안 취약점에 대한 조치가 필요합니다.

Comment on lines 20 to 44
const AnthropicIcon = () => (
<svg
fill="currentColor"
fillRule="evenodd"
height={16}
width={16}
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M13.827 3.52h3.603L24 20h-3.603l-6.57-16.48zm-7.258 0h3.767L16.906 20h-3.674l-1.343-3.461H5.017l-1.344 3.46H0L6.57 3.522zm4.132 9.959L8.453 7.687 6.205 13.48H10.7z" />
</svg>
);

const OpenAIIcon = () => (
<svg
fill="currentColor"
fillRule="evenodd"
height={16}
width={16}
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z" />
</svg>
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

AnthropicIconOpenAIIcon 컴포넌트가 copy-button.tsx 파일 내에 직접 정의되어 있어 파일이 길어지고 가독성을 해칠 수 있습니다.

이 아이콘 컴포넌트들을 별도의 파일(예: icons.tsx)로 분리하여 관리하는 것을 제안합니다. 이렇게 하면 copy-button.tsx 파일은 주 로직에 더 집중할 수 있어 코드 유지보수성이 향상됩니다.

예시:

apps/website/src/components/copy-button/icons.tsx

export const AnthropicIcon = () => (
    <svg
        fill="currentColor"
        fillRule="evenodd"
        height={16}
        width={16}
        viewBox="0 0 24 24"
        xmlns="http://www.w3.org/2000/svg"
    >
        <path d="..." />
    </svg>
);

export const OpenAIIcon = () => (
    <svg
        fill="currentColor"
        fillRule="evenodd"
        height={16}
        width={16}
        viewBox="0 0 24 24"
        xmlns="http://www.w3.org/2000/svg"
    >
        <path d="..." />
    </svg>
);

apps/website/src/components/copy-button/copy-button.tsx

import { AnthropicIcon, OpenAIIcon } from './icons';
// ...

Comment on lines +134 to +137
<HStack
gap="0"
className="rounded-md shadow-[inset_0_0_0_1px_var(--vapor-color-border-secondary)] w-fit"
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding role="group" might be worth considering.

{checked ? <ConfirmOutlineIcon /> : <CopyAsMarkdownOutlineIcon />}
마크다운 복사
</Button>
<span className="w-px bg-v-gray-200 self-stretch" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This element is used as a separator, so adding role="separator" would make it clearer.

Comment on lines +103 to +108
onClick: () => {
if (isValidMarkdownUrl(markdownUrl)) {
trackCopyButtonEvent(COPY_BUTTON_ACTIONS.VIEW_MARKDOWN, markdownUrl);
window.open(markdownUrl, '_blank');
}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "View in Markdown" item cannot be tracked when clicked. However, the other two elements trigger event tracking when clicked, even if no action occurs. This issue arises due to differences in validation locations using isValidMarkdownUrl. To ensure consistency, it would be best to standardize this behavior.

Comment on lines +98 to +131
const menuItems = useMemo(
() => [
{
label: '마크다운으로 보기',
icon: markdownIcon,
onClick: () => {
if (isValidMarkdownUrl(markdownUrl)) {
trackCopyButtonEvent(COPY_BUTTON_ACTIONS.VIEW_MARKDOWN, markdownUrl);
window.open(markdownUrl, '_blank');
}
},
isExternal: true,
},
{
label: 'Claude에게 질문하기',
icon: anthropicIcon,
onClick: () => {
trackCopyButtonEvent(COPY_BUTTON_ACTIONS.ASK_CLAUDE, markdownUrl);
openLLMChat('claude', markdownUrl);
},
isExternal: true,
},
{
label: 'ChatGPT에게 질문하기',
icon: openAIIcon,
onClick: () => {
trackCopyButtonEvent(COPY_BUTTON_ACTIONS.ASK_CHATGPT, markdownUrl);
openLLMChat('chatgpt', markdownUrl);
},
isExternal: true,
},
],
[markdownUrl],
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

menuItems should ideally only know what actions each button actually performs. Currently, each menu item handles validation, event tracking, and even the action logic it must process, making it difficult to grasp at a glance what each element does.

Therefore, I propose the following structure to separate the MenuItem handler from the actions of the buttons:

const handleMenuItemClick = (action: CopyButtonAction, handler: () => void) => {
    if (!isValidMarkdownUrl(markdownUrl)) return;
    trackCopyButtonEvent(action, markdownUrl);
    handler();
};

const menuItems = useMemo(
    () => [
        {
            label: '마크다운으로 보기',
            icon: markdownIcon,
            action: COPY_BUTTON_ACTIONS.VIEW_MARKDOWN,
            handler: () => window.open(markdownUrl, '_blank'),
            isExternal: true,
        },
        {
            label: 'Claude에게 질문하기',
            icon: anthropicIcon,
            action: COPY_BUTTON_ACTIONS.ASK_CLAUDE,
            handler: () => openLLMChat('claude', markdownUrl),
            isExternal: true,
        },
        {
            label: 'ChatGPT에게 질문하기',
            icon: openAIIcon,
            action: COPY_BUTTON_ACTIONS.ASK_CHATGPT,
            handler: () => openLLMChat('chatgpt', markdownUrl),
            isExternal: true,
        },
    ],
    [markdownUrl],
);

// ...

{
    menuItems.map(({ label, icon, action, handler, isExternal }) => (
        <Menu.Item key={label} onClick={() => handleMenuItemClick(action, handler)}>
            {icon}
            {label}
            {isExternal && <OpenInNewOutlineIcon width={16} height={16} className="ml-auto" />}
        </Menu.Item>
    ));
}

This approach should also maintain the consistency of the validation logic mentioned earlier.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tracking logic seems tightly coupled only to the Copy Button, and since these functions are directly called within the Copy Button to create the tracking functions, it feels like concerns aren't well separated.

Event tracking could be useful elsewhere too, so it might be a good idea to refactor it over time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants