Skip to content

Commit 66c6b36

Browse files
committed
feat(website): redesign site in dopejs style
1 parent 4fdab2e commit 66c6b36

15 files changed

Lines changed: 570 additions & 451 deletions

website/index.html

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,21 @@
33
<head>
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6-
<meta name="theme-color" content="#0f1117" />
6+
<meta name="theme-color" content="#fbfcff" />
77
<meta name="description" content="Multi-CLI environment switcher for Claude Code, Codex, and OpenCode with API proxy auto-failover" />
88
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
99
<title>GoZen</title>
1010
</head>
1111
<body>
12+
<script>
13+
(() => {
14+
let saved = null;
15+
try { saved = localStorage.getItem('gozen-theme'); } catch {}
16+
const dark = saved === 'dark' || (saved === null && matchMedia('(prefers-color-scheme: dark)').matches);
17+
document.documentElement.dataset.theme = dark ? 'dark' : 'light';
18+
if (dark) document.querySelector('meta[name="theme-color"]').content = '#0b1112';
19+
})();
20+
</script>
1221
<div id="root"></div>
1322
<script type="module" src="/src/main.tsx"></script>
1423
</body>

website/src/components/home/Commands.module.scss

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.section {
2-
padding: 5rem 0;
2+
padding: 4.5rem 0 6rem;
33
}
44

55
.container {
@@ -13,17 +13,16 @@
1313
}
1414

1515
.heading {
16-
margin-bottom: 3rem;
17-
text-align: center;
18-
font-size: 1.875rem;
19-
font-weight: 700;
20-
letter-spacing: -0.025em;
16+
margin: 0 0 2rem;
17+
font-size: clamp(1.65rem, 4vw, 2.25rem);
18+
font-weight: 740;
19+
letter-spacing: -0.035em;
2120
color: var(--zen-text-primary);
2221
}
2322

2423
.tableWrap {
2524
overflow-x: auto;
26-
border-radius: 0.75rem;
25+
border-radius: 14px;
2726
border: 1px solid var(--zen-border);
2827
background: var(--zen-bg-surface);
2928
-webkit-overflow-scrolling: touch;
@@ -72,12 +71,16 @@
7271
.cmdCode {
7372
white-space: nowrap;
7473
font-size: 0.875rem;
75-
color: var(--zen-teal);
74+
color: var(--zen-teal-strong);
7675
background: none;
7776
border: none;
7877
padding: 0;
7978
}
8079

80+
@media (max-width: 600px) {
81+
.section { padding: 3.5rem 0 4.5rem; }
82+
}
83+
8184
.cmdDesc {
8285
font-size: 0.875rem;
8386
color: var(--zen-text-secondary);

website/src/components/home/Commands.tsx

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,36 +31,6 @@ const commands = [
3131
{cmd: 'zen completion <shell>', key: 'completion'},
3232
];
3333

34-
const cmdDefaults: Record<string, string> = {
35-
start: 'Start CLI (uses project binding or default config)',
36-
profile: 'Start with specified profile',
37-
profilePick: 'Interactively select profile',
38-
cli: 'Use specified CLI (claude/codex/opencode)',
39-
yes: 'Auto-approve CLI permissions (claude --permission-mode acceptEdits, codex -a never)',
40-
use: 'Use specified provider directly (no proxy)',
41-
pick: 'Interactively select provider to start',
42-
list: 'List all providers and profiles',
43-
configAddProvider: 'Add a new provider',
44-
configAddProfile: 'Add a new profile',
45-
configDefaultClient: 'Set the default CLI client',
46-
configDefaultProfile: 'Set the default profile',
47-
configResetPassword: 'Reset the Web UI access password',
48-
configSync: 'Pull config from remote sync backend',
49-
daemonStart: 'Start the zend daemon',
50-
daemonStop: 'Stop the daemon',
51-
daemonStatus: 'Show daemon status',
52-
daemonEnable: 'Install daemon as system service',
53-
daemonDisable: 'Uninstall daemon system service',
54-
bind: 'Bind current directory to profile',
55-
bindCli: 'Bind current directory to specified CLI',
56-
unbind: 'Unbind current directory',
57-
status: 'Show current directory binding status',
58-
web: 'Open Web UI in browser (auto-starts daemon)',
59-
upgrade: 'Upgrade to latest version',
60-
version: 'Show version',
61-
completion: 'Generate shell completion (zsh/bash/fish)',
62-
};
63-
6434
export function Commands({t}: {readonly t: (key: UIKey) => string}) {
6535
return (
6636
<section className={styles.section}>
Lines changed: 53 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.section {
2-
padding: 5rem 0;
2+
padding: 3.5rem 0 4.5rem;
33
}
44

55
.container {
@@ -13,55 +13,48 @@
1313
}
1414

1515
.heading {
16-
margin-bottom: 3rem;
17-
text-align: center;
18-
font-size: 1.875rem;
19-
font-weight: 700;
20-
letter-spacing: -0.025em;
16+
margin: 0 0 1.5rem;
17+
font-size: clamp(1.65rem, 4vw, 2.25rem);
18+
font-weight: 740;
19+
letter-spacing: -0.035em;
2120
color: var(--zen-text-primary);
2221
}
2322

2423
.grid {
2524
display: grid;
26-
gap: 1rem;
27-
}
28-
29-
@media (min-width: 640px) {
30-
.grid { grid-template-columns: repeat(2, 1fr); }
31-
}
32-
@media (min-width: 1024px) {
33-
.grid { grid-template-columns: repeat(3, 1fr); }
25+
grid-template-columns: repeat(3, minmax(0, 1fr));
26+
overflow: hidden;
27+
border: 1px solid var(--zen-border);
28+
border-radius: 16px;
29+
background: var(--zen-bg-surface);
3430
}
3531

3632
.card {
37-
border-radius: 0.75rem;
38-
border: 1px solid var(--zen-border);
39-
background: var(--zen-bg-surface);
40-
padding: 1.5rem;
41-
transition: all 0.15s;
33+
min-height: 215px;
34+
padding: 1.65rem;
35+
border-right: 1px solid var(--zen-border);
36+
border-bottom: 1px solid var(--zen-border);
37+
transition: background-color 0.16s ease;
4238
}
4339

4440
.card:hover {
45-
border-color: var(--zen-border-strong);
46-
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
41+
background: var(--zen-bg-elevated);
4742
}
4843

49-
.iconBox {
50-
display: flex;
51-
height: 2.5rem;
52-
width: 2.5rem;
53-
align-items: center;
54-
justify-content: center;
55-
border-radius: 0.5rem;
56-
background: var(--zen-teal-dim);
57-
color: var(--zen-teal);
58-
margin-bottom: 1rem;
44+
.card:nth-child(3n) { border-right: 0; }
45+
.card:nth-child(n + 10) { border-bottom: 0; }
46+
47+
.number {
48+
color: var(--zen-teal-strong);
49+
font: 700 0.7rem/1 ui-monospace, 'SFMono-Regular', Menlo, monospace;
50+
letter-spacing: 0.06em;
5951
}
6052

6153
.cardTitle {
62-
margin-bottom: 0.5rem;
63-
font-size: 1rem;
64-
font-weight: 600;
54+
margin: 1.55rem 0 0.65rem;
55+
font-size: 1.05rem;
56+
font-weight: 700;
57+
letter-spacing: -0.02em;
6558
color: var(--zen-text-primary);
6659
}
6760

@@ -73,29 +66,34 @@
7366

7467
.comingSoon {
7568
display: flex;
76-
align-items: center;
77-
justify-content: center;
78-
border-radius: 0.75rem;
79-
border: 1px dashed var(--zen-border);
80-
background: color-mix(in srgb, var(--zen-bg-surface) 50%, transparent);
81-
padding: 1.5rem;
82-
text-align: center;
83-
}
84-
85-
.comingSoonIcon {
86-
display: flex;
87-
height: 2.5rem;
88-
width: 2.5rem;
89-
align-items: center;
90-
justify-content: center;
91-
border-radius: 0.5rem;
92-
background: var(--zen-lavender-dim);
93-
color: var(--zen-lavender);
94-
margin: 0 auto 0.75rem;
69+
grid-column: span 2;
70+
min-height: 215px;
71+
flex-direction: column;
72+
justify-content: space-between;
73+
padding: 1.65rem;
74+
background: var(--zen-bg-elevated);
9575
}
9676

9777
.comingSoonText {
98-
font-size: 0.875rem;
99-
font-weight: 500;
78+
margin: 0;
79+
font-size: 0.9rem;
80+
line-height: 1.6;
81+
font-weight: 600;
10082
color: var(--zen-text-muted);
10183
}
84+
85+
@media (max-width: 900px) {
86+
.grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
87+
.card:nth-child(3n) { border-right: 1px solid var(--zen-border); }
88+
.card:nth-child(2n) { border-right: 0; }
89+
.card:nth-child(n + 10) { border-bottom: 1px solid var(--zen-border); }
90+
.comingSoon { border-bottom: 0; }
91+
}
92+
93+
@media (max-width: 600px) {
94+
.section { padding: 2.75rem 0 3.5rem; }
95+
.grid { grid-template-columns: 1fr; }
96+
.card,
97+
.card:nth-child(3n) { min-height: 0; border-right: 0; border-bottom: 1px solid var(--zen-border); }
98+
.comingSoon { grid-column: auto; min-height: 150px; }
99+
}

website/src/components/home/Features.tsx

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,11 @@
11
import type {UIKey} from '../../locales';
2-
import {
3-
Terminal, Settings, Shield, GitBranch, FolderSymlink,
4-
Variable, Globe, RefreshCw, Sparkles, Server, Lock,
5-
} from 'lucide-react';
62
import styles from './Features.module.scss';
73

84
const featureKeys = [
9-
{key: 'multiCli', icon: Terminal},
10-
{key: 'multiConfig', icon: Settings},
11-
{key: 'daemon', icon: Server},
12-
{key: 'failover', icon: Shield},
13-
{key: 'routing', icon: GitBranch},
14-
{key: 'binding', icon: FolderSymlink},
15-
{key: 'envVars', icon: Variable},
16-
{key: 'webUi', icon: Globe},
17-
{key: 'webSecurity', icon: Lock},
18-
{key: 'configSync', icon: RefreshCw},
5+
'multiCli', 'multiConfig', 'daemon', 'failover', 'routing',
6+
'binding', 'envVars', 'webUi', 'webSecurity', 'configSync',
197
];
208

21-
const featureDefaults: Record<string, {title: string; desc: string}> = {
22-
multiCli: {title: 'Multi-CLI Support', desc: 'Support for Claude Code, Codex, and OpenCode with per-project configuration'},
23-
multiConfig: {title: 'Config Management', desc: 'Manage all API provider configs in a unified JSON file'},
24-
daemon: {title: 'Unified Daemon', desc: 'Single zend process hosts both the proxy server and the Web UI'},
25-
failover: {title: 'Proxy Failover', desc: 'Built-in HTTP proxy with automatic failover when primary provider is unavailable'},
26-
routing: {title: 'Scenario Routing', desc: 'Intelligent routing based on request characteristics (thinking, image, etc.)'},
27-
binding: {title: 'Project Bindings', desc: 'Bind directories to specific profiles and CLIs for automatic project-level configuration'},
28-
envVars: {title: 'Environment Variables', desc: 'Configure per-CLI environment variables at the provider level'},
29-
webUi: {title: 'Web Interface', desc: 'Browser-based visual management with password-protected access'},
30-
webSecurity: {title: 'Web Security', desc: 'Auto-generated password, session-based auth, RSA encryption for token transport'},
31-
configSync: {title: 'Config Sync', desc: 'Sync providers, profiles, and settings across devices with AES-256-GCM encryption'},
32-
};
33-
349
export function Features({t}: {readonly t: (key: UIKey) => string}) {
3510
return (
3611
<section className={styles.section}>
@@ -39,9 +14,9 @@ export function Features({t}: {readonly t: (key: UIKey) => string}) {
3914
{t('features.title')}
4015
</h2>
4116
<div className={styles.grid}>
42-
{featureKeys.map(({key, icon: Icon}) => (
17+
{featureKeys.map((key, index) => (
4318
<div key={key} className={styles.card}>
44-
<div className={styles.iconBox}><Icon size={20} /></div>
19+
<span className={styles.number}>{String(index + 1).padStart(2, '0')}</span>
4520
<h3 className={styles.cardTitle}>
4621
{t(`features.${key}.title` as UIKey)}
4722
</h3>
@@ -51,12 +26,8 @@ export function Features({t}: {readonly t: (key: UIKey) => string}) {
5126
</div>
5227
))}
5328
<div className={styles.comingSoon}>
54-
<div>
55-
<div className={styles.comingSoonIcon}><Sparkles size={20} /></div>
56-
<p className={styles.comingSoonText}>
57-
{t('features.comingSoon')}
58-
</p>
59-
</div>
29+
<span className={styles.number}>··</span>
30+
<p className={styles.comingSoonText}>{t('features.comingSoon')}</p>
6031
</div>
6132
</div>
6233
</div>

0 commit comments

Comments
 (0)