'use client'; import { useState } from 'react'; import { useMutation, useQuery } from 'convex/react'; import { Check, Copy, ExternalLink, Plus, Trash2 } from 'lucide-react'; import { toast } from 'sonner'; import type { Doc } from '@spoon/backend/convex/_generated/dataModel.js'; import { api } from '@spoon/backend/convex/_generated/api.js'; import { Button, Card, CardContent, CardHeader, CardTitle, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, Input, Label, } from '@spoon/ui'; const RemoteRow = ({ label, url, remoteName, onRemove, }: { label: string; url: string; remoteName?: string; onRemove?: () => Promise; }) => { const [copied, setCopied] = useState(false); const copy = async () => { try { await navigator.clipboard.writeText(url); setCopied(true); toast.success(`${label} URL copied.`); window.setTimeout(() => setCopied(false), 1800); } catch (error) { console.error(error); toast.error('Could not copy remote URL.'); } }; return (

{label}

{remoteName ? (

git remote: {remoteName}

) : null}
{onRemove ? ( ) : null}
event.currentTarget.select()} />
); }; export const SpoonClonePanel = ({ spoon }: { spoon: Doc<'spoons'> }) => { const remotes = useQuery(api.spoonRemotes.listForSpoon, { spoonId: spoon._id }) ?? []; const createRemote = useMutation(api.spoonRemotes.create); const removeRemote = useMutation(api.spoonRemotes.remove); const [label, setLabel] = useState(''); const [remoteName, setRemoteName] = useState(''); const [url, setUrl] = useState(''); const [submitting, setSubmitting] = useState(false); const [remoteDialogOpen, setRemoteDialogOpen] = useState(false); const cloneUrl = spoon.forkUrl; const addRemote = async (event: React.FormEvent) => { event.preventDefault(); setSubmitting(true); try { await createRemote({ spoonId: spoon._id, label, url, remoteName: remoteName || undefined, }); setLabel(''); setRemoteName(''); setUrl(''); setRemoteDialogOpen(false); toast.success('Remote added.'); } catch (error) { console.error(error); toast.error('Could not add remote.'); } finally { setSubmitting(false); } }; return ( Clone your fork Add git remote Store another repository URL to copy from this Spoon. GitHub remains the source of truth for upstream maintenance.
setLabel(event.target.value)} />
setRemoteName(event.target.value)} />
setUrl(event.target.value)} />
{cloneUrl ? ( <>

This GitHub fork remains Spoon's source of truth for upstream maintenance.

) : (

Add fork metadata before Spoon can show a clone URL.

)} {remotes.length ? (
{remotes.map((remote) => ( { try { await removeRemote({ remoteId: remote._id }); toast.success('Remote removed.'); } catch (error) { toast.error( error instanceof Error ? error.message : 'Could not remove remote.', ); } }} /> ))}
) : null}
); };