import { CheckIcon, CopyIcon, PlusIcon } from 'lucide-react'; import { useState } from 'react'; import { Button } from '#/components/atoms/button.jsx'; import { Notice } from '#/components/atoms/notice.jsx'; import { Dialog } from '#/components/molecules/dialog.jsx'; import { SelectField } from '#/components/molecules/select-field.jsx'; import { TextField } from '#/components/molecules/text-field.jsx'; import { xrpcPost } from '#/lib/api.js'; import { queryKeys, useApiMutation } from '#/lib/query.js'; /** The shell commands GitHub shows after creating a repository, our spelling. */ function pushInstructions(cloneUrl) { return [`git remote add origin ${cloneUrl}`, 'git push -u origin main'].join( '\n', ); } export function CommandBlock({ text }) { const [copied, setCopied] = useState(false); const copy = async () => { await navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 1500); }; return (
        {text}
      
); } /** * The push commands for a repository nothing has been pushed to. The dialog * shows these right after creating, and the repository page keeps showing * them until the first push, so closing the dialog loses nothing. * @param {{name: string, cloneUrl: string, authority: string, isPrivate: boolean}} props */ export function SetupInstructions({ name, cloneUrl, authority, isPrivate }) { return (
{isPrivate && ( This repository is private. Space members you add can read it; nobody else can. )}
Nothing has been pushed yet. From an existing local repository:
Starting from nothing:
Pushing needs the helper: npm install -g @pdsjs/git, then git-remote-atproto login {authority}.
); } /** * The dialog behind the New repository button. Creating writes an empty * repository record at once, private ones into their space, so the list and * the repository page exist before the first push. * @param {{data: {handle?: string|null, did: string, features: Record}, onNavigate: (href: string) => void}} props */ export function NewRepoDialog({ data, onNavigate }) { const authority = data.handle || data.did; const canPrivate = Boolean(data.features.spaces && data.features.spaceAdmin); const [name, setName] = useState(''); const [visibility, setVisibility] = useState('public'); /** @type {[{cloneUrl: string, space: string|null}|null, Function]} */ const [created, setCreated] = useState(null); const action = useApiMutation([ queryKeys.spaces, queryKeys.gitRepos, queryKeys.overview, ]); const validName = /^[A-Za-z0-9._~-]{1,512}$/.test(name); const submit = async (event) => { event.preventDefault(); if (!validName) return; await action.run(async () => { const payload = visibility === 'private' ? { name, private: true } : { name }; const { space } = await xrpcPost('dev.pdsjs.git.createRepo', payload); // Public or private, the address is the same: the helper finds the // space a private repository keeps under its own name. setCreated({ space, cloneUrl: `atproto://${authority}/${name}` }); }); }; const repoHref = created ? `/account/git/${encodeURIComponent(name)}${created.space ? `?space=${encodeURIComponent(created.space)}` : ''}` : ''; return ( New repository } title={created ? name : 'New repository'} > {created ? (
These commands stay on the repository page until the first push.
) : (
setName(event.target.value)} required pattern="[A-Za-z0-9._~\-]{1,512}" hint="Letters, digits, and . _ ~ -" /> setVisibility(value)} options={ canPrivate ? [ ['public', 'Public'], ['private', 'Private'], ] : [['public', 'Public']] } /> {!canPrivate && (
Private repositories need spaces enabled on this server.
)} {visibility === 'private' && (
Only the people you add can read it. Add them from the repository page.
)} {action.error && {action.error}} )}
); }