Skip to content

Commit

Permalink
improvement: add 'Copy image' button (#3006)
Browse files Browse the repository at this point in the history
  • Loading branch information
mscolnick authored Nov 29, 2024
1 parent 9130733 commit c3cc1de
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 0 deletions.
32 changes: 32 additions & 0 deletions frontend/src/components/editor/cell/cell-context-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { renderMinimalShortcut } from "@/components/shortcuts/renderShortcut";
import type { ActionButton } from "../actions/types";
import {
ClipboardCopyIcon,
ClipboardPasteIcon,
CopyIcon,
ImageIcon,
Expand All @@ -24,6 +25,7 @@ import { goToDefinitionAtCursorPosition } from "@/core/codemirror/go-to-definiti
import { CellOutputId } from "@/core/cells/ids";
import { Logger } from "@/utils/Logger";
import { copyToClipboard } from "@/utils/copy";
import { toast } from "@/components/ui/use-toast";

interface Props extends CellActionButtonProps {
children: React.ReactNode;
Expand All @@ -37,6 +39,7 @@ export const CellActionsContextMenu = ({ children, ...props }: Props) => {
const DEFAULT_CONTEXT_MENU_ITEMS: ActionButton[] = [
{
label: "Copy",
hidden: Boolean(imageRightClicked),
icon: <CopyIcon size={13} strokeWidth={1.5} />,
handle: async () => {
// Has selection, use browser copy
Expand All @@ -60,13 +63,15 @@ export const CellActionsContextMenu = ({ children, ...props }: Props) => {
},
{
label: "Cut",
hidden: Boolean(imageRightClicked),
icon: <ScissorsIcon size={13} strokeWidth={1.5} />,
handle: () => {
document.execCommand("cut");
},
},
{
label: "Paste",
hidden: Boolean(imageRightClicked),
icon: <ClipboardPasteIcon size={13} strokeWidth={1.5} />,
handle: async () => {
const { getEditorView } = props;
Expand All @@ -89,6 +94,33 @@ export const CellActionsContextMenu = ({ children, ...props }: Props) => {
}
},
},
{
label: "Copy image",
hidden: !imageRightClicked,
icon: <ClipboardCopyIcon size={13} strokeWidth={1.5} />,
handle: async () => {
if (imageRightClicked) {
const response = await fetch(imageRightClicked.src);
const blob = await response.blob();
const item = new ClipboardItem({ [blob.type]: blob });
await navigator.clipboard
.write([item])
.then(() => {
toast({
title: "Copied image to clipboard",
});
})
.catch((error) => {
toast({
title:
"Failed to copy image to clipboard. Try downloading instead.",
description: error.message,
});
Logger.error("Failed to copy image to clipboard", error);
});
}
},
},
{
icon: <ImageIcon size={13} strokeWidth={1.5} />,
label: "Download image",
Expand Down
31 changes: 31 additions & 0 deletions marimo/_smoke_tests/mpl/basic_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import marimo

__generated_with = "0.9.27"
app = marimo.App(width="medium")


@app.cell
def __():
import marimo as mo
return (mo,)


@app.cell
def __():
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 1, 100)
y = np.sin(x)

plt.plot(x, y)
plt.title("Sine Wave")
plt.xlabel("x")
plt.ylabel("sin(x)")

plt.gca()
return np, plt, x, y


if __name__ == "__main__":
app.run()

0 comments on commit c3cc1de

Please sign in to comment.