Skip to content

Comments

Add Copy button to Generate page#55

Open
Krosebrook wants to merge 1 commit intomainfrom
feat/generate-copy-lyrics-4181731622216068255
Open

Add Copy button to Generate page#55
Krosebrook wants to merge 1 commit intomainfrom
feat/generate-copy-lyrics-4181731622216068255

Conversation

@Krosebrook
Copy link
Owner

@Krosebrook Krosebrook commented Feb 16, 2026

Added a "Copy to Clipboard" button for generated lyrics in the Generate page. This micro-UX improvement allows users to easily copy lyrics without manual selection. It provides visual feedback with an icon change and a toast notification.


PR created automatically by Jules for task 4181731622216068255 started by @Krosebrook


Summary by cubic

Added a Copy button next to Save on the Generate page so users can quickly copy generated lyrics. Shows a tooltip, a Check icon when copied, and a toast; uses navigator.clipboard with a textarea fallback for older browsers.

Written for commit 71f829c. Summary will update on new commits.

Summary by CodeRabbit

  • New Features
    • Added copy-to-clipboard button with visual feedback (Check icon when copied) for generated lyrics and content.
    • Implemented status tooltips showing copy confirmation ("Copy" or "Copied!").
    • Reorganized output action buttons for improved layout alongside the Save button.

- Added a "Copy" button next to the "Save" button in the Generate page.
- Implemented `handleCopy` function with fallback for older browsers.
- Added visual feedback (Check icon) and toast notification.
- Used `Tooltip` for better accessibility.

Co-authored-by: Krosebrook <214532761+Krosebrook@users.noreply.github.com>
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copilot AI review requested due to automatic review settings February 16, 2026 05:51
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Krosebrook, 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!

This pull request significantly enhances the user experience on the Generate page by introducing a dedicated button to copy generated lyrics. This micro-UX improvement streamlines the workflow for users, providing immediate feedback and ensuring accessibility across various browser environments, ultimately making the application more intuitive and efficient for content management.

Highlights

  • Copy to Clipboard Functionality: Implemented a 'Copy to Clipboard' button on the Generate page, allowing users to easily copy generated lyrics without manual selection.
  • User Experience Improvements: Added visual feedback for the copy action, including an icon change (from 'Copy' to 'Check') and a toast notification confirming the lyrics have been copied.
  • Browser Compatibility: Included a fallback mechanism for older browsers that do not support the navigator.clipboard API, ensuring broader compatibility for the copy functionality.
Changelog
  • client/src/pages/Generate.tsx
    • Imported Copy and Check icons from lucide-react for the copy button's visual feedback.
    • Imported Tooltip components (Tooltip, TooltipContent, TooltipTrigger) to provide hover information for the copy button.
    • Added a new state variable hasCopied to manage the visual state of the copy button.
    • Implemented the handleCopy asynchronous function, which copies the current lyrics to the clipboard using navigator.clipboard.writeText with a fallback for older browsers using a temporary textarea.
    • Integrated the new copy button into the UI, positioned next to the 'Save to Library' button, and wrapped both in a flex container.
    • Configured the copy button to display either the Copy or Check icon based on the hasCopied state and to show a tooltip with relevant text.
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.

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

📝 Walkthrough

Walkthrough

The Generate page component now includes clipboard copy functionality. Users can copy generated lyrics or content via a new copy button with visual feedback. Copy and Check icons from lucide-react, along with Tooltip components, were imported and integrated into the UI across two action areas.

Changes

Cohort / File(s) Summary
Clipboard Copy Feature
client/src/pages/Generate.tsx
Added hasCopied state and handleCopy function for copying currentLyrics or generatedContent to clipboard with toast feedback. Imported Copy, Check icons from lucide-react and Tooltip component. Replaced single Save button with action group containing copy button (with tooltip showing Copy/Copied! status and conditional icon display) and existing Save to Library button in both top-right and bottom action areas.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰✨ With whiskers twitching, I hop with glee,
A copy button swift as can be!
From lyric lines to content bright,
To clipboard they leap in one click's flight.
A toast-y cheer and icons that gleam,
Making sharing easier than a dream! 📋✂️

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Copy button to Generate page' directly and clearly describes the main change in the PR—adding a copy-to-clipboard button feature to the Generate page, which is the primary focus of all modifications.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/generate-copy-lyrics-4181731622216068255

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 1 file

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

This pull request adds a "Copy to Clipboard" button to the Generate page, which is a great micro-UX improvement. The implementation includes visual feedback and a fallback for older browsers. My review includes a suggestion to refactor the copy handler to reduce code duplication and improve maintainability.

Comment on lines +48 to +81
const handleCopy = async () => {
const textToCopy = currentLyrics || generatedContent;
if (!textToCopy) return;

// Fallback for older browsers
if (!navigator.clipboard) {
const textArea = document.createElement("textarea");
textArea.value = textToCopy;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
setHasCopied(true);
setTimeout(() => setHasCopied(false), 2000);
toast({ description: "Lyrics copied to clipboard" });
} catch (err) {
console.error('Unable to copy to clipboard', err);
}
document.body.removeChild(textArea);
return;
}

try {
await navigator.clipboard.writeText(textToCopy);
setHasCopied(true);
setTimeout(() => setHasCopied(false), 2000);
toast({
description: "Lyrics copied to clipboard",
});
} catch (err) {
console.error('Failed to copy text: ', err);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The handleCopy function has duplicated logic for showing a success state. This logic is repeated for handling modern and fallback clipboard operations. To improve maintainability and adhere to the DRY (Don't Repeat Yourself) principle, this logic can be extracted into a helper function. Additionally, the fallback implementation can be made more robust by checking the boolean return value of document.execCommand('copy').

  const handleCopy = async () => {
    const textToCopy = currentLyrics || generatedContent;
    if (!textToCopy) return;

    const onCopySuccess = () => {
      setHasCopied(true);
      setTimeout(() => setHasCopied(false), 2000);
      toast({ description: "Lyrics copied to clipboard" });
    };

    // Fallback for older browsers
    if (!navigator.clipboard) {
      const textArea = document.createElement("textarea");
      textArea.value = textToCopy;
      document.body.appendChild(textArea);
      textArea.focus();
      textArea.select();
      try {
        if (document.execCommand('copy')) {
          onCopySuccess();
        }
      } catch (err) {
        console.error('Unable to copy to clipboard', err);
      }
      document.body.removeChild(textArea);
      return;
    }

    try {
      await navigator.clipboard.writeText(textToCopy);
      onCopySuccess();
    } catch (err) {
      console.error('Failed to copy text: ', err);
    }
  };

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 71f829c417

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +60 to +63
document.execCommand('copy');
setHasCopied(true);
setTimeout(() => setHasCopied(false), 2000);
toast({ description: "Lyrics copied to clipboard" });

Choose a reason for hiding this comment

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

P2 Badge Gate success toast on execCommand result

In the fallback path (used when navigator.clipboard is unavailable), document.execCommand('copy') can return false without throwing, but this code still sets hasCopied and shows a success toast unconditionally. In browsers where the command is blocked or fails silently, users will be told the lyrics were copied even though the clipboard did not change; check the boolean return value and only show success UI when it is true (with an error toast otherwise).

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@client/src/pages/Generate.tsx`:
- Around line 48-81: The handleCopy function currently logs errors to console
only; update both the fallback path (the document.execCommand block) and the
navigator.clipboard.writeText catch block to show a user-facing toast on failure
(use the existing toast function) in addition to logging, e.g., call toast with
a clear error description like "Failed to copy lyrics to clipboard" and
optionally include minimal error info; ensure you still clear the temporary
textarea in the fallback and do not change the success behavior
(setHasCopied/setTimeout).
🧹 Nitpick comments (1)
client/src/pages/Generate.tsx (1)

53-68: Consider wrapping fallback textarea cleanup in a finally block.

If textArea.focus() or textArea.select() throws unexpectedly, removeChild on line 67 would be skipped, leaving a hidden textarea in the DOM. Moving the cleanup into finally makes it resilient.

Proposed fix
     if (!navigator.clipboard) {
        const textArea = document.createElement("textarea");
        textArea.value = textToCopy;
+       textArea.style.position = "fixed";
+       textArea.style.opacity = "0";
        document.body.appendChild(textArea);
-       textArea.focus();
-       textArea.select();
        try {
+         textArea.focus();
+         textArea.select();
          document.execCommand('copy');
          setHasCopied(true);
          setTimeout(() => setHasCopied(false), 2000);
          toast({ description: "Lyrics copied to clipboard" });
        } catch (err) {
          console.error('Unable to copy to clipboard', err);
+         toast({ variant: "destructive", description: "Failed to copy lyrics" });
+       } finally {
+         document.body.removeChild(textArea);
        }
-       document.body.removeChild(textArea);
        return;
     }

This also adds position: fixed; opacity: 0 to prevent the textarea from causing a brief layout shift.

Comment on lines +48 to +81
const handleCopy = async () => {
const textToCopy = currentLyrics || generatedContent;
if (!textToCopy) return;

// Fallback for older browsers
if (!navigator.clipboard) {
const textArea = document.createElement("textarea");
textArea.value = textToCopy;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
setHasCopied(true);
setTimeout(() => setHasCopied(false), 2000);
toast({ description: "Lyrics copied to clipboard" });
} catch (err) {
console.error('Unable to copy to clipboard', err);
}
document.body.removeChild(textArea);
return;
}

try {
await navigator.clipboard.writeText(textToCopy);
setHasCopied(true);
setTimeout(() => setHasCopied(false), 2000);
toast({
description: "Lyrics copied to clipboard",
});
} catch (err) {
console.error('Failed to copy text: ', err);
}
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing user-facing error feedback on copy failure.

Both the modern clipboard API path (line 78-79) and the fallback path (line 64-65) only log to console.error without notifying the user. As per coding guidelines, "Use toast notifications for user feedback in React components."

Proposed fix
       } catch (err) {
         console.error('Unable to copy to clipboard', err);
+        toast({ variant: "destructive", description: "Failed to copy lyrics" });
       }
       document.body.removeChild(textArea);
       return;
     }

     try {
       await navigator.clipboard.writeText(textToCopy);
       setHasCopied(true);
       setTimeout(() => setHasCopied(false), 2000);
       toast({
         description: "Lyrics copied to clipboard",
       });
     } catch (err) {
       console.error('Failed to copy text: ', err);
+      toast({ variant: "destructive", description: "Failed to copy lyrics" });
     }
🤖 Prompt for AI Agents
In `@client/src/pages/Generate.tsx` around lines 48 - 81, The handleCopy function
currently logs errors to console only; update both the fallback path (the
document.execCommand block) and the navigator.clipboard.writeText catch block to
show a user-facing toast on failure (use the existing toast function) in
addition to logging, e.g., call toast with a clear error description like
"Failed to copy lyrics to clipboard" and optionally include minimal error info;
ensure you still clear the temporary textarea in the fallback and do not change
the success behavior (setHasCopied/setTimeout).

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a "Copy to Clipboard" button to the Generate page, allowing users to copy generated lyrics with a single click. The implementation provides visual feedback through an icon change (Copy → Check) and displays a toast notification upon successful copy.

Changes:

  • Added clipboard copy functionality with fallback support for older browsers
  • Introduced a new icon button with tooltip next to the "Save to Library" button
  • Implemented visual feedback state (hasCopied) with automatic reset after 2 seconds

Comment on lines +48 to +81
const handleCopy = async () => {
const textToCopy = currentLyrics || generatedContent;
if (!textToCopy) return;

// Fallback for older browsers
if (!navigator.clipboard) {
const textArea = document.createElement("textarea");
textArea.value = textToCopy;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
setHasCopied(true);
setTimeout(() => setHasCopied(false), 2000);
toast({ description: "Lyrics copied to clipboard" });
} catch (err) {
console.error('Unable to copy to clipboard', err);
}
document.body.removeChild(textArea);
return;
}

try {
await navigator.clipboard.writeText(textToCopy);
setHasCopied(true);
setTimeout(() => setHasCopied(false), 2000);
toast({
description: "Lyrics copied to clipboard",
});
} catch (err) {
console.error('Failed to copy text: ', err);
}
};
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

This clipboard copy logic is duplicated from SongDetails.tsx (lines 53-70). The repo already has this exact pattern in at least two places (Generate and SongDetails). Consider extracting this into a reusable hook like useClipboard in client/src/hooks/use-clipboard.ts to avoid duplication and make maintenance easier. The hook should accept text and success message as parameters, return a copy function and hasCopied state, and handle both modern and legacy clipboard APIs consistently.

Copilot uses AI. Check for mistakes.
setTimeout(() => setHasCopied(false), 2000);
toast({ description: "Lyrics copied to clipboard" });
} catch (err) {
console.error('Unable to copy to clipboard', err);
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The fallback error handling in the execCommand path doesn't provide user feedback when copy fails. In the catch block at line 65, consider adding a toast notification with variant "destructive" to inform the user of the failure, similar to how SongDetails.tsx handles this scenario (line 68 in SongDetails.tsx shows the pattern). This ensures consistent error feedback across both clipboard API paths.

Suggested change
console.error('Unable to copy to clipboard', err);
console.error('Unable to copy to clipboard', err);
toast({
description: "Failed to copy lyrics to clipboard. Please try again.",
variant: "destructive",
});

Copilot uses AI. Check for mistakes.
description: "Lyrics copied to clipboard",
});
} catch (err) {
console.error('Failed to copy text: ', err);
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

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

The modern clipboard API error path (catch at line 78) only logs the error to console but doesn't provide user feedback via toast. Add a toast notification with variant "destructive" to inform users when the copy operation fails, ensuring consistent user experience with the fallback path and matching the pattern used in SongDetails.tsx.

Suggested change
console.error('Failed to copy text: ', err);
console.error("Failed to copy text: ", err);
toast({
title: "Copy failed",
description: "Could not copy lyrics to your clipboard. Please try again.",
variant: "destructive",
});

Copilot uses AI. Check for mistakes.
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.

1 participant