bhg-helper 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -0
- package/api/app.ts +53 -0
- package/api/index.ts +9 -0
- package/api/lib/logger.ts +65 -0
- package/api/lib/paths.ts +27 -0
- package/api/lib/providers.ts +66 -0
- package/api/lib/repository.ts +153 -0
- package/api/lib/types.ts +43 -0
- package/api/relay/config.ts +76 -0
- package/api/relay/protocol.ts +393 -0
- package/api/relay/server.ts +283 -0
- package/api/routes/backups.ts +73 -0
- package/api/routes/config.ts +197 -0
- package/api/routes/install.ts +158 -0
- package/api/routes/logs.ts +20 -0
- package/api/routes/providers.ts +13 -0
- package/api/routes/relay.ts +106 -0
- package/api/server.ts +40 -0
- package/cli/cli.js +454 -0
- package/dist/assets/index-BjvGHrGe.js +156 -0
- package/dist/assets/index-CQrGCyBr.css +1 -0
- package/dist/favicon.svg +4 -0
- package/dist/index.html +20 -0
- package/index.html +19 -0
- package/nodemon.json +10 -0
- package/package.json +82 -0
- package/postcss.config.js +10 -0
- package/scripts/install.bat +32 -0
- package/scripts/start.bat +46 -0
- package/scripts/start.ps1 +45 -0
- package/src/App.tsx +73 -0
- package/src/assets/react.svg +1 -0
- package/src/components/ConsolePanel.tsx +44 -0
- package/src/components/Empty.tsx +8 -0
- package/src/components/ErrorBoundary.tsx +54 -0
- package/src/components/Layout.tsx +17 -0
- package/src/components/Page.tsx +130 -0
- package/src/components/Sidebar.tsx +56 -0
- package/src/hooks/useTheme.ts +29 -0
- package/src/index.css +1350 -0
- package/src/lib/api.ts +120 -0
- package/src/lib/store.ts +166 -0
- package/src/lib/types.ts +117 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +10 -0
- package/src/pages/ConsolePage.tsx +48 -0
- package/src/pages/Dashboard.tsx +101 -0
- package/src/pages/Install.tsx +195 -0
- package/src/pages/Relay.tsx +409 -0
- package/src/vite-env.d.ts +1 -0
- package/tailwind.config.js +13 -0
- package/tsconfig.json +40 -0
- package/vite.config.ts +28 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { useStore } from '../lib/store'
|
|
2
|
+
import type { LogEntry } from '../lib/types'
|
|
3
|
+
|
|
4
|
+
interface PageProps {
|
|
5
|
+
title: string
|
|
6
|
+
code: string
|
|
7
|
+
subtitle?: string
|
|
8
|
+
actions?: React.ReactNode
|
|
9
|
+
children: React.ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function Page({ title, code, subtitle, actions, children }: PageProps) {
|
|
13
|
+
const saving = useStore((s) => s.saving)
|
|
14
|
+
const lastSavedAt = useStore((s) => s.lastSavedAt)
|
|
15
|
+
const error = useStore((s) => s.errorMessage)
|
|
16
|
+
const clearError = useStore((s) => s.clearError)
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="page">
|
|
20
|
+
<header className="page-header">
|
|
21
|
+
<div style={{ flex: 1 }}>
|
|
22
|
+
<h1 className="page-title">
|
|
23
|
+
<span className="page-title-prefix">[{code}]</span>
|
|
24
|
+
{title}
|
|
25
|
+
</h1>
|
|
26
|
+
{subtitle && <p className="page-subtitle">{subtitle}</p>}
|
|
27
|
+
</div>
|
|
28
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--sp-3)' }}>
|
|
29
|
+
<span className="led">
|
|
30
|
+
<span className={'led-dot ' + (saving ? 'amber' : 'green')} />
|
|
31
|
+
{saving ? 'SYNCING' : 'IDLE'}
|
|
32
|
+
</span>
|
|
33
|
+
{lastSavedAt && (
|
|
34
|
+
<span
|
|
35
|
+
style={{
|
|
36
|
+
fontFamily: 'var(--font-mono)',
|
|
37
|
+
fontSize: 10,
|
|
38
|
+
color: 'var(--ink-3)',
|
|
39
|
+
textTransform: 'uppercase',
|
|
40
|
+
letterSpacing: '0.1em',
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
LAST SAVE · {new Date(lastSavedAt).toLocaleTimeString()}
|
|
44
|
+
</span>
|
|
45
|
+
)}
|
|
46
|
+
{actions && <div className="page-actions">{actions}</div>}
|
|
47
|
+
</div>
|
|
48
|
+
</header>
|
|
49
|
+
|
|
50
|
+
{error && (
|
|
51
|
+
<div
|
|
52
|
+
style={{
|
|
53
|
+
margin: 'var(--sp-4) var(--sp-7) 0',
|
|
54
|
+
padding: 'var(--sp-3) var(--sp-4)',
|
|
55
|
+
background: 'rgba(248, 81, 73, 0.08)',
|
|
56
|
+
border: '1px solid var(--accent-red-dim)',
|
|
57
|
+
color: 'var(--accent-red)',
|
|
58
|
+
fontFamily: 'var(--font-mono)',
|
|
59
|
+
fontSize: 12,
|
|
60
|
+
display: 'flex',
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
gap: 'var(--sp-3)',
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
<span style={{ fontWeight: 600 }}>ERR</span>
|
|
66
|
+
<span style={{ flex: 1 }}>{error}</span>
|
|
67
|
+
<button className="btn btn-ghost" onClick={clearError} style={{ color: 'var(--accent-red)' }}>
|
|
68
|
+
✕
|
|
69
|
+
</button>
|
|
70
|
+
</div>
|
|
71
|
+
)}
|
|
72
|
+
|
|
73
|
+
<div className="page-body">{children}</div>
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function Panel({
|
|
79
|
+
title,
|
|
80
|
+
children,
|
|
81
|
+
}: {
|
|
82
|
+
title: string
|
|
83
|
+
children: React.ReactNode
|
|
84
|
+
}) {
|
|
85
|
+
return (
|
|
86
|
+
<div className="panel">
|
|
87
|
+
<div className="panel-header">{title}</div>
|
|
88
|
+
<div className="panel-body">{children}</div>
|
|
89
|
+
</div>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function StatusCard({
|
|
94
|
+
label,
|
|
95
|
+
value,
|
|
96
|
+
meta,
|
|
97
|
+
tone = 'amber',
|
|
98
|
+
}: {
|
|
99
|
+
label: string
|
|
100
|
+
value: string | React.ReactNode
|
|
101
|
+
meta?: string
|
|
102
|
+
tone?: 'amber' | 'green' | 'red' | 'gray'
|
|
103
|
+
}) {
|
|
104
|
+
return (
|
|
105
|
+
<div className={'status-card ' + tone}>
|
|
106
|
+
<div className="status-card-label">
|
|
107
|
+
<span className={'led-dot ' + tone} /> {label}
|
|
108
|
+
</div>
|
|
109
|
+
<div className="status-card-value">{value}</div>
|
|
110
|
+
{meta && <div className="status-card-meta">{meta}</div>}
|
|
111
|
+
</div>
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function CopyableText({ text, max = 36 }: { text: string; max?: number }) {
|
|
116
|
+
const display = text.length > max ? text.slice(0, max) + '…' : text
|
|
117
|
+
return (
|
|
118
|
+
<span
|
|
119
|
+
className="cell-mono copyable"
|
|
120
|
+
title={text}
|
|
121
|
+
onClick={() => {
|
|
122
|
+
if (navigator.clipboard) navigator.clipboard.writeText(text)
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
{display}
|
|
126
|
+
</span>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export type { LogEntry }
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { NavLink } from 'react-router-dom'
|
|
2
|
+
import { LayoutDashboard, Terminal, Server } from 'lucide-react'
|
|
3
|
+
import { useStore } from '../lib/store'
|
|
4
|
+
|
|
5
|
+
const links = [
|
|
6
|
+
{ to: '/', label: '主控台', icon: LayoutDashboard, code: '01' },
|
|
7
|
+
{ to: '/relay', label: '中转服务', icon: Server, code: '02', highlight: true },
|
|
8
|
+
{ to: '/console', label: '事件日志', icon: Terminal, code: '03' },
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
export default function Sidebar() {
|
|
12
|
+
const config = useStore((s) => s.config)
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<aside className="app-sidebar">
|
|
16
|
+
<div className="sidebar-brand">
|
|
17
|
+
<div className="sidebar-brand-row">
|
|
18
|
+
<div className="sidebar-logo">D</div>
|
|
19
|
+
<div>
|
|
20
|
+
<div className="sidebar-title">DeepSeek Bridge</div>
|
|
21
|
+
<div className="sidebar-subtitle">Claude Code → DeepSeek</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
<div className="sidebar-meta">
|
|
25
|
+
<div className="sidebar-meta-row">
|
|
26
|
+
<span className="key">env</span>
|
|
27
|
+
<span className="val">127.0.0.1:8787</span>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<nav className="sidebar-nav">
|
|
33
|
+
<div className="sidebar-section">— Modules</div>
|
|
34
|
+
{links.map(({ to, label, icon: Icon, code }) => (
|
|
35
|
+
<NavLink
|
|
36
|
+
key={to}
|
|
37
|
+
to={to}
|
|
38
|
+
end={to === '/'}
|
|
39
|
+
className={({ isActive }) => 'sidebar-link' + (isActive ? ' active' : '')}
|
|
40
|
+
>
|
|
41
|
+
<Icon size={16} strokeWidth={1.5} />
|
|
42
|
+
<span>{label}</span>
|
|
43
|
+
<span className="badge">{code}</span>
|
|
44
|
+
</NavLink>
|
|
45
|
+
))}
|
|
46
|
+
</nav>
|
|
47
|
+
|
|
48
|
+
<div className="sidebar-footer">
|
|
49
|
+
<span className="led">
|
|
50
|
+
<span className="led-dot green" /> ONLINE
|
|
51
|
+
</span>
|
|
52
|
+
<span style={{ marginLeft: 'auto' }}>v1.0</span>
|
|
53
|
+
</div>
|
|
54
|
+
</aside>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
type Theme = 'light' | 'dark';
|
|
4
|
+
|
|
5
|
+
export function useTheme() {
|
|
6
|
+
const [theme, setTheme] = useState<Theme>(() => {
|
|
7
|
+
const savedTheme = localStorage.getItem('theme') as Theme;
|
|
8
|
+
if (savedTheme) {
|
|
9
|
+
return savedTheme;
|
|
10
|
+
}
|
|
11
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
document.documentElement.classList.remove('light', 'dark');
|
|
16
|
+
document.documentElement.classList.add(theme);
|
|
17
|
+
localStorage.setItem('theme', theme);
|
|
18
|
+
}, [theme]);
|
|
19
|
+
|
|
20
|
+
const toggleTheme = () => {
|
|
21
|
+
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
theme,
|
|
26
|
+
toggleTheme,
|
|
27
|
+
isDark: theme === 'dark'
|
|
28
|
+
};
|
|
29
|
+
}
|