mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
docs: recommend npx @twsxtd/hapi as primary installation method
Update documentation to promote npm package usage over binary downloads: - Changed primary quickstart command to use npx @twsxtd/hapi - Moved binary download to alternative option with link to releases - Updated Multi-agent support section to use npx commands - Simplified Quickstart section title - Adjusted step numbering to reflect changes
This commit is contained in:
@@ -13,30 +13,30 @@ Run Claude Code / Codex / Gemini sessions locally and control them remotely thro
|
||||
- Track session progress with todo lists.
|
||||
- Supports multiple AI backends: Claude Code, Codex, and Gemini.
|
||||
|
||||
## Quickstart (single executable)
|
||||
## Quickstart
|
||||
|
||||
1. Download the prebuilt `hapi` binary for your platform and put it on your PATH.
|
||||
|
||||
2. Start the server on a machine you control:
|
||||
1. Start the server on a machine you control:
|
||||
|
||||
```bash
|
||||
hapi server
|
||||
npx @twsxtd/hapi server
|
||||
```
|
||||
|
||||
3. If the server has no public IP, expose it over HTTPS:
|
||||
> Alternatively, download the prebuilt binary from [Releases](https://github.com/tiann/hapi/releases) and run `hapi server`.
|
||||
|
||||
2. If the server has no public IP, expose it over HTTPS:
|
||||
- Cloudflare Tunnel: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/
|
||||
- Tailscale: https://tailscale.com/kb/
|
||||
|
||||
4. Run the CLI on the machine where you want sessions:
|
||||
3. Run the CLI on the machine where you want sessions:
|
||||
|
||||
```bash
|
||||
# If the server is not on localhost:3006
|
||||
export HAPI_BOT_URL="https://your-domain.example"
|
||||
|
||||
hapi
|
||||
npx @twsxtd/hapi
|
||||
```
|
||||
|
||||
5. Open the UI in a browser at the server URL and log in with `CLI_API_TOKEN`.
|
||||
4. Open the UI in a browser at the server URL and log in with `CLI_API_TOKEN`.
|
||||
|
||||
## Telegram Mini App (optional)
|
||||
|
||||
@@ -65,9 +65,9 @@ ALLOWED_CHAT_IDS="12345678"
|
||||
|
||||
## Multi-agent support
|
||||
|
||||
- `hapi` - Start a Claude Code session.
|
||||
- `hapi codex` - Start an OpenAI Codex session.
|
||||
- `hapi gemini` - Start a Google Gemini session.
|
||||
- `npx @twsxtd/hapi` - Start a Claude Code session.
|
||||
- `npx @twsxtd/hapi codex` - Start an OpenAI Codex session.
|
||||
- `npx @twsxtd/hapi gemini` - Start a Google Gemini session.
|
||||
|
||||
## CLI config file
|
||||
|
||||
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
# Loading 元素精简优化计划
|
||||
|
||||
## 一、当前 Loading 元素汇总
|
||||
|
||||
| 位置 | 文件 | 当前样式 | 展示时机 |
|
||||
|------|------|----------|----------|
|
||||
| 同步横幅 | `SyncingBanner.tsx` | 顶部固定横幅 + 旋转符号 | `isSyncing && isOnline` |
|
||||
| 权限按钮 | `PermissionFooter.tsx:66-73` | SVG SpinnerIcon | `loading` 状态 |
|
||||
| 认证加载 | `App.tsx:132-146` | 纯文本 "Loading…" / "Authorizing…" | `isAuthSourceLoading` / `isAuthLoading` |
|
||||
| 会话加载 | `router.tsx:100-106` | 纯文本 "Loading session…" | `!session` |
|
||||
| 消息加载 | `HappyThread.tsx:235-258` | 纯文本 "Loading..." + 按钮文本 | `isLoadingMessages` / `isLoadingMoreMessages` |
|
||||
| Git 状态 | `files.tsx:313-318` | 纯文本 "Loading Git status..." / "Loading files..." | `gitLoading` / `searchResults.isLoading` |
|
||||
| 文件内容 | `file.tsx:231-232` | 纯文本 "Loading file..." | `diffQuery.isLoading \|\| fileQuery.isLoading` |
|
||||
| 登录按钮 | `LoginPrompt.tsx:77` | 按钮文本 "Signing in..." | `isLoading` |
|
||||
| 创建会话 | `NewSession.tsx:119-122, 216` | "Loading machines..." / "Creating..." | `isPending` |
|
||||
| 问卷提交 | `AskUserQuestionFooter.tsx:385` | 按钮文本 "Submitting…" | `loading` |
|
||||
|
||||
## 二、问题
|
||||
|
||||
1. **样式不统一** - 纯文本 vs SpinnerIcon vs 旋转符号
|
||||
2. **SpinnerIcon 无法复用** - 定义在 PermissionFooter 内部
|
||||
3. **省略号不一致** - "..." vs "…"
|
||||
|
||||
## 三、精简方案
|
||||
|
||||
**核心原则:Spinner 做基础元件,LoadingState 负责语义化加载**
|
||||
|
||||
### 创建唯一的 Spinner 组件(可访问性就绪)
|
||||
|
||||
```tsx
|
||||
// web/src/components/Spinner.tsx
|
||||
export function Spinner({
|
||||
size = 'md',
|
||||
className,
|
||||
label = 'Loading',
|
||||
}: {
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
className?: string
|
||||
label?: string
|
||||
}) {
|
||||
const sizeClasses = { sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6' }
|
||||
return (
|
||||
<svg
|
||||
className={cn(sizeClasses[size], 'animate-spin text-[var(--app-hint)]', className)}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
role="status"
|
||||
aria-label={label}
|
||||
>
|
||||
<circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="2.5" opacity="0.25" />
|
||||
<path d="M21 12a9 9 0 0 0-9-9" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" opacity="0.75" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 新增 LoadingState(Spinner + 可选文案)
|
||||
|
||||
```tsx
|
||||
// web/src/components/LoadingState.tsx
|
||||
export function LoadingState({
|
||||
label = 'Loading…',
|
||||
className,
|
||||
spinnerSize = 'md',
|
||||
}: {
|
||||
label?: string
|
||||
className?: string
|
||||
spinnerSize?: 'sm' | 'md' | 'lg'
|
||||
}) {
|
||||
return (
|
||||
<div className={cn('inline-flex items-center gap-2 text-[var(--app-hint)]', className)} role="status" aria-live="polite">
|
||||
<Spinner size={spinnerSize} label={label} />
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### SyncingBanner 处理
|
||||
|
||||
**方案:不改,避免打扰用户**
|
||||
- 保留顶部横幅与 `useSyncingState` 逻辑
|
||||
- 若需要统一视觉,仅替换内部图标为新 `Spinner`
|
||||
|
||||
## 四、实施步骤
|
||||
|
||||
### Step 1: 创建 Spinner 组件(可访问性)
|
||||
- [ ] 创建 `web/src/components/Spinner.tsx`,包含 `role="status"` 与 `aria-label`
|
||||
|
||||
### Step 2: 新增 LoadingState 组件
|
||||
- [ ] 创建 `web/src/components/LoadingState.tsx`
|
||||
- [ ] 统一使用 `Loading…`(省略号用 `…`)
|
||||
|
||||
### Step 3: 替换加载场景(保留语义)
|
||||
- [ ] `App.tsx` - 认证相关使用 `<LoadingState label="Authorizing…" />`
|
||||
- [ ] `router.tsx` - 会话加载使用 `<LoadingState label="Loading session…" />`
|
||||
- [ ] `HappyThread.tsx` - 消息加载用 `<LoadingState label="Loading…" />`
|
||||
- [ ] `files.tsx` - Git/文件列表加载用 `<LoadingState label="Loading files…" />`
|
||||
- [ ] `file.tsx` - 文件内容加载用 `<LoadingState label="Loading file…" />`
|
||||
- [ ] `PermissionFooter.tsx` - 使用新 `Spinner`,删除内部 `SpinnerIcon`
|
||||
|
||||
### Step 4: 按钮加载保持可读标签
|
||||
- [ ] `LoginPrompt.tsx` / `NewSession.tsx` / `AskUserQuestionFooter.tsx` - 保留文字,旁边加 `Spinner size="sm"`
|
||||
- [ ] 按钮添加 `aria-busy`,避免替换成纯图标
|
||||
|
||||
### Step 5: 列表/内容区优先 Skeleton 或占位
|
||||
- [ ] `HappyThread.tsx` - 消息列表加载改为 skeleton/placeholder
|
||||
- [ ] `files.tsx` - 文件列表加载改为 skeleton/placeholder
|
||||
- [ ] `file.tsx` - 文件内容加载改为 skeleton/placeholder
|
||||
|
||||
### Step 6: SyncingBanner
|
||||
- [ ] 保留 `SyncingBanner.tsx`
|
||||
- [ ] 可选:内部图标替换为新 `Spinner`,不改交互与布局
|
||||
|
||||
## 五、关键文件
|
||||
|
||||
**新建:**
|
||||
- `web/src/components/Spinner.tsx`
|
||||
- `web/src/components/LoadingState.tsx`
|
||||
|
||||
**修改:**
|
||||
- `web/src/App.tsx`
|
||||
- `web/src/router.tsx`
|
||||
- `web/src/components/AssistantChat/HappyThread.tsx`
|
||||
- `web/src/routes/sessions/files.tsx`
|
||||
- `web/src/routes/sessions/file.tsx`
|
||||
- `web/src/components/ToolCard/PermissionFooter.tsx`
|
||||
- `web/src/components/LoginPrompt.tsx`
|
||||
- `web/src/components/NewSession.tsx`
|
||||
- `web/src/components/ToolCard/AskUserQuestionFooter.tsx`
|
||||
- `web/src/components/SyncingBanner.tsx`
|
||||
Reference in New Issue
Block a user