Skip to content

Commit df610ed

Browse files
feat: fork config button on public page
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-Claude) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent dc60762 commit df610ed

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed

src/routes/[username]/[slug]/+page.svelte

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
<script lang="ts">
22
import type { PageData } from './$types';
33
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
4+
import { goto } from '$app/navigation';
45
56
let { data }: { data: PageData } = $props();
67
78
let activeTab = $state('overview');
89
let copied = $state(false);
910
let showShareModal = $state(false);
1011
let shareCopied = $state(false);
12+
let forking = $state(false);
13+
let forkError = $state('');
1114
1215
function getInstallCommand() {
1316
return `curl -fsSL https://openboot.dev/${data.configUser.username}/${data.config.slug} | bash`;
@@ -285,6 +288,45 @@ ${rects}
285288
img.src = url;
286289
}
287290
291+
async function forkConfig() {
292+
forking = true;
293+
forkError = '';
294+
295+
try {
296+
const authCheck = await fetch('/api/user');
297+
if (!authCheck.ok) {
298+
window.location.href = '/api/auth/login';
299+
return;
300+
}
301+
302+
const forkResponse = await fetch('/api/configs', {
303+
method: 'POST',
304+
headers: { 'Content-Type': 'application/json' },
305+
body: JSON.stringify({
306+
name: `${data.config.name} (fork)`,
307+
description: `Forked from @${data.configUser.username}`,
308+
base_preset: data.config.base_preset,
309+
packages: data.config.packages,
310+
is_public: true,
311+
custom_script: data.config.custom_script || '',
312+
dotfiles_repo: data.config.dotfiles_repo || ''
313+
})
314+
});
315+
316+
if (!forkResponse.ok) {
317+
const error = await forkResponse.json();
318+
forkError = error.error || 'Failed to fork config';
319+
forking = false;
320+
return;
321+
}
322+
323+
goto('/dashboard');
324+
} catch (err) {
325+
forkError = 'Network error. Please try again.';
326+
forking = false;
327+
}
328+
}
329+
288330
function getPackageUrl(name: string, type: 'formula' | 'cask' | 'npm'): string {
289331
if (type === 'npm') return `https://www.npmjs.com/package/${name}`;
290332
if (type === 'cask') return `https://formulae.brew.sh/cask/${name}`;
@@ -363,6 +405,13 @@ ${rects}
363405
{copied ? 'Copied!' : 'Copy'}
364406
</button>
365407
</div>
408+
<button class="fork-btn" onclick={forkConfig} disabled={forking}>
409+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><circle cx="18" cy="6" r="3"/><path d="M18 9v2c0 .6-.4 1-1 1H7c-.6 0-1-.4-1-1V9"/><path d="M12 12v3"/></svg>
410+
{forking ? 'Forking...' : 'Fork Config'}
411+
</button>
412+
{#if forkError}
413+
<div class="fork-error">{forkError}</div>
414+
{/if}
366415
</div>
367416

368417
<div class="stats">
@@ -392,6 +441,10 @@ ${rects}
392441
<span class="stat-label">Preferences</span>
393442
</div>
394443
{/if}
444+
<div class="stat">
445+
<span class="stat-value">{data.config.install_count || 0}</span>
446+
<span class="stat-label">Installs</span>
447+
</div>
395448
</div>
396449
</section>
397450

@@ -796,6 +849,47 @@ ${rects}
796849
background: var(--accent-hover);
797850
}
798851
852+
.fork-btn {
853+
display: flex;
854+
align-items: center;
855+
justify-content: center;
856+
gap: 8px;
857+
width: 100%;
858+
margin-top: 12px;
859+
padding: 10px 16px;
860+
background: var(--bg-tertiary);
861+
border: 1px solid var(--border);
862+
border-radius: 8px;
863+
color: var(--text-primary);
864+
font-size: 0.9rem;
865+
font-weight: 500;
866+
font-family: inherit;
867+
cursor: pointer;
868+
transition: all 0.2s;
869+
}
870+
871+
.fork-btn:hover:not(:disabled) {
872+
border-color: var(--accent);
873+
color: var(--accent);
874+
background: var(--bg-secondary);
875+
}
876+
877+
.fork-btn:disabled {
878+
opacity: 0.6;
879+
cursor: not-allowed;
880+
}
881+
882+
.fork-error {
883+
margin-top: 8px;
884+
padding: 8px 12px;
885+
background: rgba(239, 68, 68, 0.1);
886+
border: 1px solid rgba(239, 68, 68, 0.3);
887+
border-radius: 6px;
888+
color: #ef4444;
889+
font-size: 0.85rem;
890+
text-align: center;
891+
}
892+
799893
.stats {
800894
display: flex;
801895
justify-content: center;

0 commit comments

Comments
 (0)