diff --git a/src/components/repo/RepoCloneUrl.tsx b/src/components/repo/RepoCloneUrl.tsx
new file mode 100644
index 0000000..da9d751
--- /dev/null
+++ b/src/components/repo/RepoCloneUrl.tsx
@@ -0,0 +1,55 @@
+import type { Handle } from '@atcute/lexicons'
+import { useEffect, useState } from 'react'
+import { getRepoName, type Repo } from '../../lib/tangled'
+
+type RepoCloneUrlProps = {
+ handle: Handle
+ repo: Repo
+}
+
+export function RepoCloneUrl({ handle, repo }: RepoCloneUrlProps) {
+ const [status, setStatus] = useState<'idle' | 'copied' | 'failed'>('idle')
+ const cloneUrl = buildCloneUri(handle, repo)
+
+ async function copyCloneUrl() {
+ try {
+ await navigator.clipboard.writeText(cloneUrl)
+ setStatus('copied')
+ } catch {
+ setStatus('failed')
+ }
+ }
+
+ useEffect(() => {
+ if (status !== 'copied') return
+
+ const timeoutId = window.setTimeout(() => {
+ setStatus('idle')
+ }, 2000)
+
+ return () => {
+ window.clearTimeout(timeoutId)
+ }
+ }, [status])
+
+ return (
+
+ Clone
+
+ {cloneUrl}
+
+
+
+ )
+}
+
+function buildCloneUri(handle: Handle, repo: Repo) {
+ const name = getRepoName(repo)
+ return `git@tangled.org:${handle}/${name}.git`
+}
diff --git a/src/components/repo/RepoView.tsx b/src/components/repo/RepoView.tsx
index 0ec1ff2..fc35509 100644
--- a/src/components/repo/RepoView.tsx
+++ b/src/components/repo/RepoView.tsx
@@ -1,16 +1,19 @@
+import type { Handle } from '@atcute/lexicons'
import Markdown from 'react-markdown'
import rehypeRaw from 'rehype-raw'
import rehypeSanitize from 'rehype-sanitize'
-import type { Repo } from '../../lib/tangled/index'
+import type { Repo } from '../../lib/tangled'
import { getRepoName } from '../../lib/tangled/repo'
import { SurfaceCard } from '../shared/SurfaceCard'
+import { RepoCloneUrl } from './RepoCloneUrl'
import { RepoStarCount } from './RepoStarCount'
type RepoProps = {
+ handle: Handle
repo: Repo
}
-export function RepoView({ repo }: RepoProps) {
+export function RepoView({ handle, repo }: RepoProps) {
const { value } = repo
const name = getRepoName(repo)
const repoDid = value.repoDid
@@ -49,6 +52,8 @@ export function RepoView({ repo }: RepoProps) {
)}
+
+
)
}
diff --git a/src/pages/RepoPage.tsx b/src/pages/RepoPage.tsx
index cc615f5..f375734 100644
--- a/src/pages/RepoPage.tsx
+++ b/src/pages/RepoPage.tsx
@@ -5,14 +5,14 @@ import type { $output as MiniDoc } from '@atcute/microcosm/types/blue/microcosm/
import type { $output as RepoTreeResponse } from '@atcute/tangled/types/repo/tree'
import { useEffect, useState } from 'react'
import { useParams, useSearchParams } from 'react-router-dom'
-import { RepoPageSkeleton } from '../components/shared/PageSkeletons'
import { PageContainer } from '../components/layout/PageContainer'
import { ProfileByline } from '../components/profile/ProfileByline'
import { RepoReadme } from '../components/repo/RepoReadme'
-import { RepoTabs } from '../components/repo/RepoTabs'
import { parseRepoSection } from '../components/repo/repoSections'
+import { RepoTabs } from '../components/repo/RepoTabs'
import { RepoView } from '../components/repo/RepoView'
import { RepoWorkspace } from '../components/repo/RepoWorkspace'
+import { RepoPageSkeleton } from '../components/shared/PageSkeletons'
import { SurfaceCard } from '../components/shared/SurfaceCard'
import { getProfile as getBskyProfile } from '../lib/bsky/actor'
import { getMiniDoc } from '../lib/microcosm'
@@ -106,7 +106,7 @@ export function RepoPage() {
/>