diff --git a/src/lib/components/ConfigCard.svelte b/src/lib/components/ConfigCard.svelte
index c2fb6d2..a8d4a1d 100644
--- a/src/lib/components/ConfigCard.svelte
+++ b/src/lib/components/ConfigCard.svelte
@@ -20,8 +20,6 @@
onaction: (action: string, slug: string) => void;
} = $props();
- let copiedCmd = $state(false);
-
const packages = $derived(
Array.isArray(config.packages)
? config.packages.map((p: any) =>
@@ -30,52 +28,22 @@
: []
);
- const stats = $derived({
- total: packages.length,
- apps: packages.filter((p: any) => p.type === 'cask').length,
- cli: packages.filter((p: any) => p.type !== 'cask' && p.type !== 'npm').length,
- npm: packages.filter((p: any) => p.type === 'npm').length,
- });
+ const cli = $derived(packages.filter((p: any) => p.type !== 'cask' && p.type !== 'npm').length);
+ const apps = $derived(packages.filter((p: any) => p.type === 'cask').length);
- const installCmd = $derived(
- config.alias
- ? `openboot install ${config.alias}`
- : `openboot install ${username}/${config.slug}`
+ const installUrl = $derived(
+ config.alias ? `openboot.dev/${config.alias}` : `openboot.dev/${username}/${config.slug}`
);
- // Subtle accent color per card
- const ACCENTS = [
- '99, 102, 241', // indigo
- '236, 72, 153', // pink
- '59, 130, 246', // blue
- '16, 185, 129', // emerald
- '245, 158, 11', // amber
- '139, 92, 246', // violet
- '20, 184, 166', // teal
- '244, 63, 94', // rose
- ];
-
- function hashStr(s: string): number {
- let h = 0;
- for (let i = 0; i < s.length; i++) {
- h = ((h << 5) - h) + s.charCodeAt(i);
- h |= 0;
- }
- return Math.abs(h);
- }
-
- const accent = $derived(ACCENTS[hashStr(config.name) % ACCENTS.length]);
-
- function copyCommand(e: MouseEvent) {
- e.stopPropagation();
- navigator.clipboard.writeText(installCmd);
- copiedCmd = true;
- setTimeout(() => (copiedCmd = false), 2000);
+ function formatDate(dateStr?: string): string {
+ if (!dateStr) return '';
+ const date = new Date(dateStr.endsWith('Z') ? dateStr : dateStr + 'Z');
+ if (isNaN(date.getTime())) return '';
+ return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
}
const menuItems = $derived([
{ label: 'Duplicate', action: 'duplicate' },
- { label: 'Share', action: 'share' },
{ label: 'Export JSON', action: 'export' },
...(config.visibility !== 'public'
? [{ label: 'Push to Community', action: 'push' }]
@@ -84,203 +52,106 @@
]);
-
onaction('edit', config.slug)}
- onkeydown={(e) => e.key === 'Enter' && onaction('edit', config.slug)}
- role="button"
- tabindex="0"
->
-
-
-
-
-
-
-
-
-
{config.name}
-
- {#if config.alias}
- /{config.alias}
- {:else}
- /{config.slug}
- {/if}
-
-
-
e.stopPropagation()} role="presentation">
-
- {config.visibility}
-
- onaction(action, config.slug)} />
-
-
+
+
+
{config.name}
+
+ {config.visibility}
+
+
- {#if config.description}
-
{config.description}
- {/if}
+ {#if config.description}
+
{config.description}
+ {/if}
+
+
-
-
-
- {stats.total}
- packages
-
- {#if stats.apps > 0}
-
- {stats.apps}
- apps
-
- {/if}
- {#if stats.cli > 0}
-
- {stats.cli}
- cli
-
- {/if}
- {#if stats.npm > 0}
-
- {stats.npm}
- npm
-
- {/if}
-
+
+ $ curl -fsSL {installUrl} | bash
+
-
-
e.stopPropagation()} role="presentation">
-
-
-
-
-
$ {installCmd}
-
-
-
+
+
+
+
onaction(action, config.slug)} />
diff --git a/src/lib/components/ContextMenu.svelte b/src/lib/components/ContextMenu.svelte
index 9cf3e1c..0b97f22 100644
--- a/src/lib/components/ContextMenu.svelte
+++ b/src/lib/components/ContextMenu.svelte
@@ -36,13 +36,7 @@
-
+
{#if open}
{#each items as item, i}
@@ -70,20 +64,23 @@
display: flex;
align-items: center;
justify-content: center;
- width: 32px;
- height: 32px;
- background: none;
- border: 1px solid transparent;
- border-radius: 8px;
+ padding: 8px 12px;
+ background: var(--bg-tertiary);
+ border: 1px solid var(--border);
+ border-radius: 7px;
color: var(--text-muted);
cursor: pointer;
- transition: all 0.15s;
+ font-family: inherit;
+ font-size: 0.9rem;
+ line-height: 1;
+ transition:
+ border-color 0.15s ease,
+ color 0.15s ease;
}
.trigger:hover {
- background: var(--bg-tertiary);
- border-color: var(--border);
- color: var(--text-secondary);
+ border-color: var(--border-hover);
+ color: var(--accent);
}
.dropdown {
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index d23d990..3c34a1b 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -64,17 +64,8 @@
$
curl -fsSL openboot.dev/install.sh | bash
-
@@ -95,7 +86,17 @@
-

+
✓ Checksum verified · Added to PATH
+
=== OpenBoot Installer v0.20.0 ===
+
[DRY-RUN MODE — no changes will be made]
+
=== Step 1: Git configuration ===
+
✓ Already configured: jam <jam@hey.com>
+
=== Step 2: Preset selection ===
+
Choose your preset
+
› developer — Node, Go, Docker, VS Code
+
full — languages, DevOps, databases, AI
+
minimal — CLI essentials only
+
↑ up · ↓ down · / filter · ⏎ submit
@@ -178,7 +179,7 @@
/* ---------- hero ---------- */
.hero {
position: relative;
- padding: 120px 0 124px;
+ padding: 108px 0 110px;
display: grid;
grid-template-columns: 1.05fr 0.95fr;
gap: 60px;
@@ -194,9 +195,9 @@
linear-gradient(var(--border) 1px, transparent 1px),
linear-gradient(90deg, var(--border) 1px, transparent 1px);
background-size: 46px 46px;
- -webkit-mask-image: radial-gradient(ellipse 75% 55% at 22% 28%, #000 0%, transparent 68%);
- mask-image: radial-gradient(ellipse 75% 55% at 22% 28%, #000 0%, transparent 68%);
- opacity: 0.4;
+ -webkit-mask-image: radial-gradient(ellipse 75% 55% at 22% 30%, #000 0%, transparent 68%);
+ mask-image: radial-gradient(ellipse 75% 55% at 22% 30%, #000 0%, transparent 68%);
+ opacity: 0.45;
}
.hero-content {
@@ -216,9 +217,9 @@
}
.hero-title {
- font-size: clamp(2.3rem, 4.6vw, 3.7rem);
+ font-size: clamp(2.3rem, 4.6vw, 3.4rem);
font-weight: 500;
- line-height: 1.04;
+ line-height: 1.05;
letter-spacing: -0.04em;
color: var(--text-primary);
margin: 0 0 26px;
@@ -233,14 +234,14 @@
color: var(--text-secondary);
line-height: 1.75;
margin: 0 0 32px;
- max-width: 54ch;
+ max-width: 52ch;
}
.features-list {
list-style: none;
display: grid;
gap: 12px;
- margin: 0 0 38px;
+ margin: 0 0 36px;
padding: 0;
}
@@ -269,11 +270,11 @@
background: var(--bg-secondary);
border: 1px solid var(--border-hover);
border-radius: 10px;
- padding: 16px 17px;
+ padding: 15px 16px;
display: flex;
align-items: center;
gap: 13px;
- max-width: 510px;
+ max-width: 500px;
font-size: 0.9rem;
transition:
border-color 0.18s ease,
@@ -306,19 +307,25 @@
background: transparent;
border: none;
color: var(--text-muted);
- padding: 2px;
+ padding: 0;
cursor: pointer;
transition: color 0.15s ease;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
+ font-family: inherit;
+ font-size: 0.78rem;
}
.copy-btn:hover {
color: var(--accent);
}
+ .copy-btn.copied {
+ color: var(--accent);
+ }
+
.install-alt {
color: var(--text-muted);
font-size: 0.78rem;
@@ -340,7 +347,7 @@
.terminal-window {
width: 100%;
- max-width: 620px;
+ max-width: 560px;
border-radius: 12px;
overflow: hidden;
background: var(--bg-secondary);
@@ -387,21 +394,89 @@
.terminal-body {
background: var(--bg-secondary);
+ padding: 18px 18px 20px;
+ font-size: 0.76rem;
+ line-height: 1.85;
+ color: var(--text-secondary);
}
- .terminal-body img {
- width: 100%;
- display: block;
+ .t-line {
+ white-space: nowrap;
+ }
+
+ .t-accent {
+ color: var(--accent);
+ }
+
+ .t-muted {
+ color: var(--text-muted);
+ }
+
+ .t-strong {
+ color: var(--text-primary);
+ }
+
+ .t-head {
+ color: var(--accent);
+ margin-top: 10px;
+ }
+
+ .t-note {
+ color: var(--text-muted);
+ margin-top: 8px;
+ }
+
+ .t-ok {
+ margin-top: 4px;
+ }
+
+ .t-prompt-label {
+ color: var(--amber);
+ margin-top: 6px;
+ }
+
+ .t-sel {
+ margin-top: 2px;
+ }
+
+ .t-opt {
+ color: var(--text-muted);
+ padding-left: 14px;
+ }
+
+ .t-keys {
+ color: var(--text-muted);
+ margin-top: 10px;
+ }
+
+ .t-caret {
+ display: inline-block;
+ width: 7px;
+ height: 1em;
+ background: var(--accent);
+ translate: 0 2px;
+ animation: ob-blink 1s infinite;
+ }
+
+ @keyframes ob-blink {
+ 0%,
+ 49% {
+ opacity: 1;
+ }
+ 50%,
+ 100% {
+ opacity: 0;
+ }
}
/* ---------- how it works ---------- */
.how-it-works {
- padding: 112px 0;
+ padding: 104px 0;
border-top: 1px solid var(--border);
}
.section-header {
- margin-bottom: 60px;
+ margin-bottom: 56px;
}
.section-prompt {
@@ -435,7 +510,7 @@
.step {
background: var(--bg-primary);
- padding: 30px 26px 34px;
+ padding: 28px 24px 32px;
transition: background 0.2s ease;
}
diff --git a/src/routes/[username]/+page.svelte b/src/routes/[username]/+page.svelte
index 8d8ddd5..2ec05d4 100644
--- a/src/routes/[username]/+page.svelte
+++ b/src/routes/[username]/+page.svelte
@@ -1,11 +1,9 @@
@@ -99,131 +85,91 @@
-
-
-
-
+
{#if data.profileUser.avatar_url}
-

+

{:else}
-
{data.profileUser.username.charAt(0).toUpperCase()}
+
{data.profileUser.username.charAt(0).toLowerCase()}
{/if}
-
-
@{data.profileUser.username}
-
+
+
@{data.profileUser.username}
+
openboot.dev/{data.profileUser.username}
{#if copiedUrl}
Copied!
{/if}
-
-
Member since {formatDate(data.profileUser.created_at)}
-
+
+
member since {formatDate(data.profileUser.created_at)}
+
-
-
-
{data.configs.length}
-
Configs
+
+
{data.configs.length}
+
configs
-
-
-
{formatInstallCount(data.totalInstalls)}
-
Installs
+
+
{formatInstallCount(data.totalInstalls)}
+
installs
-
-
-
{totalPackages}
-
Packages
+
+
{totalPackages}
+
packages
{#if data.configs.length === 0}
{:else}
-
-
- {#each data.configs as config, index}
-