Skip to content

Commit 330ed71

Browse files
committed
v14.0-F: 登录UI移入设置菜单+删顶部横条+retainContextWhenHidden+TRAE修复
1 parent 2a9f4d7 commit 330ed71

3 files changed

Lines changed: 124 additions & 119 deletions

File tree

extension/src/extension.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ export function activate(context: vscode.ExtensionContext): void {
2626
// 注册侧边栏
2727
const sidebarProvider = new SidebarProvider(context.extensionUri, client, context);
2828
context.subscriptions.push(
29-
vscode.window.registerWebviewViewProvider(SidebarProvider.viewType, sidebarProvider),
29+
vscode.window.registerWebviewViewProvider(SidebarProvider.viewType, sidebarProvider, {
30+
webviewOptions: { retainContextWhenHidden: true },
31+
}),
3032
);
3133

3234
// 尝试连接 WebSocket

extension/src/sidebarProvider.ts

Lines changed: 85 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
117117
case "getSuggestions":
118118
await this._handleGetSuggestions(msg);
119119
break;
120+
case "openExternal":
121+
if (msg.url && typeof msg.url === 'string' && msg.url.startsWith('https://')) {
122+
vscode.env.openExternal(vscode.Uri.parse(msg.url));
123+
}
124+
break;
120125
}
121126
});
122127

@@ -1624,73 +1629,42 @@ ${commonRules}`;
16241629
.step-num { color: var(--info); min-width: 20px; }
16251630
.step-desc { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
16261631
.step-time { color: var(--info); font-size: 10px; min-width: 36px; text-align: right; }
1627-
/* 认证面板 */
1628-
.auth-panel {
1629-
padding: 16px 12px;
1630-
}
1631-
.auth-panel h2 {
1632-
font-size: 15px; margin: 0 0 12px; text-align: center;
1632+
/* 设置菜单内登录子面板 */
1633+
.sdrop-auth-panel {
1634+
display: none; padding: 8px 12px;
1635+
border-top: 1px solid var(--input-border);
16331636
}
1634-
.auth-panel .auth-input {
1637+
.sdrop-auth-panel input {
16351638
width: 100%; box-sizing: border-box;
1636-
padding: 7px 10px; margin-bottom: 8px;
1639+
padding: 5px 7px; margin-bottom: 5px;
16371640
background: var(--input-bg); color: var(--input-fg);
1638-
border: 1px solid var(--input-border); border-radius: 4px;
1639-
font-size: 13px; outline: none;
1641+
border: 1px solid var(--input-border); border-radius: 3px;
1642+
font-size: 11px; outline: none;
16401643
}
1641-
.auth-panel .auth-input:focus { border-color: var(--btn-bg); }
1642-
.auth-panel .auth-btn {
1643-
width: 100%; padding: 8px; margin-top: 4px;
1644+
.sdrop-auth-panel input:focus { border-color: var(--btn-bg); }
1645+
.sdrop-login-btn {
1646+
flex: 1; padding: 5px; font-size: 11px; font-weight: 600;
16441647
background: var(--btn-bg); color: var(--btn-fg);
1645-
border: none; border-radius: 4px; cursor: pointer;
1646-
font-size: 13px; font-weight: 600;
1647-
}
1648-
.auth-panel .auth-btn:hover { background: var(--btn-hover); }
1649-
.auth-panel .auth-toggle {
1650-
display: block; text-align: center; margin-top: 12px;
1651-
font-size: 12px; color: var(--btn-bg); cursor: pointer;
1652-
text-decoration: underline; background: none; border: none;
1653-
}
1654-
.auth-panel .auth-error {
1655-
color: var(--error); font-size: 12px; margin: 6px 0; text-align: center;
1656-
display: none;
1657-
}
1658-
.auth-user-bar {
1659-
display: flex; align-items: center; justify-content: space-between;
1660-
padding: 6px 12px; font-size: 12px;
1661-
border-bottom: 1px solid var(--input-border);
1648+
border: none; border-radius: 3px; cursor: pointer;
1649+
}
1650+
.sdrop-login-btn:hover { background: var(--btn-hover); }
1651+
.sdrop-register-btn {
1652+
padding: 5px 8px; font-size: 11px;
1653+
background: none; color: var(--btn-bg);
1654+
border: 1px solid var(--btn-bg); border-radius: 3px; cursor: pointer;
16621655
}
1663-
.auth-user-bar .username { color: var(--success); font-weight: 600; }
1664-
.auth-user-bar .logout-btn {
1665-
background: none; border: none; color: var(--muted);
1666-
cursor: pointer; font-size: 11px; text-decoration: underline;
1656+
.sdrop-register-btn:hover { background: var(--btn-bg); color: var(--btn-fg); }
1657+
.auth-error {
1658+
color: var(--error); font-size: 11px; margin: 0 0 5px; display: none;
1659+
}
1660+
.sdrop-user-row {
1661+
padding: 6px 12px; font-size: 11px;
1662+
border-top: 1px solid var(--input-border);
16671663
}
1668-
.auth-user-bar .logout-btn:hover { color: var(--error); }
16691664
</style>
16701665
</head>
16711666
<body>
1672-
<!-- 认证:已登录用户栏 -->
1673-
<div id="authUserBar" class="auth-user-bar" style="display:none">
1674-
<span>👤 <span class="username" id="authUsername"></span>
1675-
<span style="color:var(--muted);font-size:11px" id="authPlan"></span></span>
1676-
<span id="authCredits" style="font-size:11px;color:var(--muted);margin-left:4px"></span>
1677-
<button class="logout-btn" onclick="doLogout()">退出登录</button>
1678-
</div>
1679-
<!-- 认证:登录/注册面板 -->
1680-
<div id="authPanel" class="auth-panel" style="display:none">
1681-
<h2 id="authTitle">登录 TestPilot AI</h2>
1682-
<div id="authError" class="auth-error"></div>
1683-
<input id="authEmail" class="auth-input" type="text"
1684-
placeholder="邮箱" style="display:none" />
1685-
<input id="authUser" class="auth-input" type="text"
1686-
placeholder="用户名或邮箱" />
1687-
<input id="authPass" class="auth-input" type="password"
1688-
placeholder="密码" onkeydown="if(event.key==='Enter')doAuth()" />
1689-
<button class="auth-btn" onclick="doAuth()">登录</button>
1690-
<button class="auth-toggle" onclick="toggleAuthMode()">
1691-
没有账号?注册
1692-
</button>
1693-
</div>
1667+
16941668
<!-- 引擎状态 -->
16951669
<div class="section">
16961670
<!-- 标题行:引擎字 + 齿轮设置按钮(随页面滚动) -->
@@ -1720,9 +1694,27 @@ ${commonRules}`;
17201694
<span>🔤 字体大小</span>
17211695
<span class="badge-soon">即将推出</span>
17221696
</div>
1723-
<div class="sdrop-item disabled">
1724-
<span>🔑 账户登录</span>
1725-
<span class="badge-soon">即将推出</span>
1697+
<div class="sdrop-divider"></div>
1698+
<div class="sdrop-item" id="settingsAuthRow" onclick="toggleSettingsAuth()">
1699+
<span id="settingsAuthLabel">🔑 账户登录</span>
1700+
<span id="settingsAuthArrow" style="font-size:10px;color:var(--muted)">▾</span>
1701+
</div>
1702+
<div id="settingsAuthPanel" class="sdrop-auth-panel">
1703+
<div id="authError" class="auth-error"></div>
1704+
<input id="authUser" type="text" placeholder="用户名或邮箱" autocomplete="username" />
1705+
<input id="authPass" type="password" placeholder="密码" autocomplete="current-password" onkeydown="if(event.key==='Enter')doAuth()" />
1706+
<div style="display:flex;gap:6px;margin-top:4px">
1707+
<button class="sdrop-login-btn" onclick="doAuth()">登录</button>
1708+
<button class="sdrop-register-btn" onclick="openRegister()">前往注册 ↗</button>
1709+
</div>
1710+
</div>
1711+
<div id="settingsUserRow" class="sdrop-user-row" style="display:none">
1712+
<div style="display:flex;align-items:center;justify-content:space-between">
1713+
<span style="color:var(--success);font-weight:600">👤 <span id="authUsername"></span>
1714+
<span style="color:var(--muted)" id="authPlan"></span></span>
1715+
<button onclick="doLogout()" style="background:none;border:none;color:var(--muted);cursor:pointer;font-size:11px;text-decoration:underline">退出</button>
1716+
</div>
1717+
<div id="authCredits" style="font-size:11px;color:var(--muted);margin-top:2px"></div>
17261718
</div>
17271719
</div>
17281720
</div>
@@ -1928,80 +1920,65 @@ ${commonRules}`;
19281920
<script nonce="${nonce}">
19291921
const vscode = acquireVsCodeApi();
19301922
1931-
// ── 认证逻辑 ──────────────────────────────
1932-
let authMode = 'login'; // 'login' | 'register'
1933-
const authPanel = document.getElementById('authPanel');
1934-
const authUserBar = document.getElementById('authUserBar');
1935-
const authTitle = document.getElementById('authTitle');
1936-
const authEmail = document.getElementById('authEmail');
1923+
// ── 认证逻辑(登录UI位于设置菜单内) ──────────────────────────────
19371924
const authUser = document.getElementById('authUser');
19381925
const authPass = document.getElementById('authPass');
19391926
const authError = document.getElementById('authError');
19401927
const authUsername = document.getElementById('authUsername');
19411928
const authPlan = document.getElementById('authPlan');
19421929
19431930
function showAuthError(msg) {
1944-
authError.textContent = msg;
1945-
authError.style.display = msg ? 'block' : 'none';
1946-
}
1947-
1948-
function toggleAuthMode() {
1949-
authMode = authMode === 'login' ? 'register' : 'login';
1950-
if (authMode === 'register') {
1951-
authTitle.textContent = '注册 TestPilot AI';
1952-
authEmail.style.display = 'block';
1953-
authEmail.placeholder = '邮箱';
1954-
authUser.placeholder = '用户名';
1955-
document.querySelector('.auth-btn').textContent = '注册';
1956-
document.querySelector('.auth-toggle').textContent = '已有账号?登录';
1957-
} else {
1958-
authTitle.textContent = '登录 TestPilot AI';
1959-
authEmail.style.display = 'none';
1960-
authUser.placeholder = '用户名或邮箱';
1961-
document.querySelector('.auth-btn').textContent = '登录';
1962-
document.querySelector('.auth-toggle').textContent = '没有账号?注册';
1963-
}
1964-
showAuthError('');
1931+
if (authError) { authError.textContent = msg; authError.style.display = msg ? 'block' : 'none'; }
1932+
}
1933+
1934+
function toggleSettingsAuth() {
1935+
const panel = document.getElementById('settingsAuthPanel');
1936+
const arrow = document.getElementById('settingsAuthArrow');
1937+
const open = panel && panel.style.display === 'block';
1938+
if (panel) panel.style.display = open ? 'none' : 'block';
1939+
if (arrow) arrow.textContent = open ? '▾' : '▴';
1940+
if (!open) setTimeout(() => authUser && authUser.focus(), 50);
1941+
}
1942+
1943+
function openRegister() {
1944+
vscode.postMessage({ command: 'openExternal', url: 'https://testpilotai.pages.dev/register' });
19651945
}
19661946
19671947
function doAuth() {
19681948
showAuthError('');
1969-
if (authMode === 'login') {
1970-
const u = authUser.value.trim();
1971-
const p = authPass.value;
1972-
if (!u || !p) { showAuthError('请填写用户名和密码'); return; }
1973-
vscode.postMessage({ command: 'login', emailOrUsername: u, password: p });
1974-
} else {
1975-
const e = authEmail.value.trim();
1976-
const u = authUser.value.trim();
1977-
const p = authPass.value;
1978-
if (!e || !u || !p) { showAuthError('请填写所有字段'); return; }
1979-
if (p.length < 6) { showAuthError('密码至少6位'); return; }
1980-
vscode.postMessage({ command: 'register', email: e, username: u, password: p });
1981-
}
1949+
const u = authUser.value.trim();
1950+
const p = authPass.value;
1951+
if (!u || !p) { showAuthError('请填写用户名和密码'); return; }
1952+
vscode.postMessage({ command: 'login', emailOrUsername: u, password: p });
19821953
}
19831954
19841955
function doLogout() {
19851956
vscode.postMessage({ command: 'logout' });
19861957
}
19871958
19881959
function setAuthState(loggedIn, user) {
1960+
const authPanel = document.getElementById('settingsAuthPanel');
1961+
const userRow = document.getElementById('settingsUserRow');
1962+
const authLabel = document.getElementById('settingsAuthLabel');
1963+
const authArrow = document.getElementById('settingsAuthArrow');
19891964
if (loggedIn && user) {
1990-
authPanel.style.display = 'none';
1991-
authUserBar.style.display = 'flex';
1992-
authUsername.textContent = user.username || '';
1993-
authPlan.textContent = user.plan ? '(' + user.plan + ')' : '';
1994-
// 积分显示
1965+
if (authPanel) authPanel.style.display = 'none';
1966+
if (userRow) userRow.style.display = 'block';
1967+
if (authLabel) authLabel.textContent = '👤 ' + (user.username || '');
1968+
if (authArrow) authArrow.style.display = 'none';
1969+
if (authUsername) authUsername.textContent = user.username || '';
1970+
if (authPlan) authPlan.textContent = user.plan ? '(' + user.plan + ')' : '';
19951971
const creditsEl = document.getElementById('authCredits');
19961972
if (creditsEl && user.credits !== undefined) {
19971973
const low = user.credits < 10;
19981974
creditsEl.textContent = '💰 ' + user.credits + '分';
19991975
creditsEl.style.color = low ? 'var(--error)' : 'var(--muted)';
2000-
creditsEl.title = '剩余积分:' + user.credits + '(每10步消耗 1 积分)';
20011976
}
20021977
} else {
2003-
authPanel.style.display = 'block';
2004-
authUserBar.style.display = 'none';
1978+
if (authPanel) authPanel.style.display = 'none';
1979+
if (userRow) userRow.style.display = 'none';
1980+
if (authLabel) authLabel.textContent = '🔑 账户登录';
1981+
if (authArrow) { authArrow.textContent = '▾'; authArrow.style.display = ''; }
20051982
}
20061983
}
20071984

开发备忘录.md

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# TestPilot AI 开发备忘录
22

3-
> 最后更新:2026-03-24(✅ v14.0-A/B/C 完成:MySQL RDS + Plugin 登录 + 云端分享
3+
> 最后更新:2026-03-25(✅ v14.0-F 完成:登录UI迷你横条 + TRAE双版本冲突修复 + retainContextWhenHidden
44
> 项目结构详见 README.md,此处不重复。
55
66
---
77

8-
## v14.0 进度总结(2026-03-24
8+
## v14.0 进度总结(2026-03-25
99

1010
### ✅ v14.0-A MySQL 云数据库完成
1111
- **Alibaba Cloud RDS MySQL 8.0.36** 配置完毕
@@ -27,8 +27,15 @@
2727
- 社区 tab 整合分享表单 + 建议展示
2828
- 流程:测试完成(固Bug) → 自动弹窗 → 用户确认 → 云端上传 + 展示社区建议
2929

30-
### ⏳ v14.0-D 积分系统(已完成)
31-
### ⏳ v14.0-E 安全增强(已完成)
30+
### ✅ v14.0-D 积分系统(已完成)
31+
### ✅ v14.0-E 安全增强(已完成)
32+
33+
### ✅ v14.0-F 登录UI升级 + TRAE修复(2026-03-25)
34+
- **登录UI改为迷你横条**:删除大面板 `.auth-panel`,改为单行状态条 `.auth-mini-bar` + 折叠下拉表单 `.auth-login-dropdown`
35+
- **注册改为跳官网**:点击「前往注册」→ `openExternal` 打开 `https://testpilotai.pages.dev/register`,不再内嵌注册表单
36+
- **修复引擎未连接日志反复打印**`extension.ts` 新增 `retainContextWhenHidden: true`,防止 WebView 被销毁/重建导致 `hasLoggedDisconnected` 变量重置
37+
- **修复 TRAE 显示旧版**:TRAE 扩展目录中存在两个版本(`testpilot-ai.testpilot-ai-1.2.9` 旧 publisher + `wenzhouxinzao.testpilot-ai-1.3.0`),将旧版目录 rename 为 `.bak`,安装新版
38+
- **服务器 alembic 分支冲突修复**`stamp f03fbc681fc5` 跳过重复建表,再 `upgrade 3f8a9b2c1d47` 建 3 个安全表(login_attempts/refresh_tokens/email_verifications)
3239

3340
---
3441

@@ -61,22 +68,40 @@ cp .env.example .env # 编辑 .env 填入 API 密钥,并确保 TP_SERVER_REL
6168
poetry run python main.py # 验证:访问 http://127.0.0.1:8900/api/v1/health
6269
```
6370

64-
#### IDE 插件安装(VS Code / Windsurf / Cursor)
71+
#### IDE 插件安装(VS Code / Windsurf / Cursor / TRAE
6572
```bash
6673
cd extension
67-
npm install && npm run compile
68-
npx vsce package --allow-missing-repository
74+
npm install && node esbuild.js
75+
npx @vscode/vsce package --no-yarn
6976
# VS Code
7077
"/Applications/开发工具/Visual Studio Code.app/Contents/Resources/app/bin/code" \
71-
--install-extension testpilot-ai-1.0.0.vsix
78+
--install-extension testpilot-ai-1.3.0.vsix
7279
# Windsurf
7380
"/Applications/开发工具/Windsurf.app/Contents/Resources/app/bin/windsurf" \
74-
--install-extension testpilot-ai-1.0.0.vsix
81+
--install-extension testpilot-ai-1.3.0.vsix
7582
# Cursor
7683
"/Applications/开发工具/Cursor.app/Contents/Resources/app/bin/code" \
77-
--install-extension testpilot-ai-1.0.0.vsix
84+
--install-extension testpilot-ai-1.3.0.vsix
85+
# TRAE(Linux/macOS 同理)
86+
"/Applications/Trae.app/Contents/MacOS/Trae" \
87+
--install-extension testpilot-ai-1.3.0.vsix --force
7888
```
7989

90+
**TRAE 注意事项(⚠️ 重要):**
91+
- TRAE 扩展目录(Windows):`C:\Users\%USERNAME%\.trae-cn\extensions\`
92+
- 目录名格式:`{publisher}.{extensionName}-{version}`,publisher 不同 = 完全不同的插件
93+
- 若 TRAE 同时存在旧版(不同 publisher ID),需先将旧版目录 rename 为 `.bak`
94+
```powershell
95+
cd "$env:USERPROFILE\.trae-cn\extensions"
96+
Rename-Item testpilot-ai.testpilot-ai-1.2.9 testpilot-ai.testpilot-ai-1.2.9.bak
97+
```
98+
- Windows TRAE 安装新版:
99+
```powershell
100+
& "$env:USERPROFILE\AppData\Local\Programs\Trae\Trae.exe" `
101+
--install-extension D:\Projects\TestPilotAI\extension\testpilot-ai-1.3.0.vsix --force
102+
```
103+
- 安装后必须重启 TRAE 才生效
104+
80105
#### MCP 配置(Mac 路径版)
81106
```bash
82107
# 查询 Poetry 虚拟环境 Python 路径
@@ -248,6 +273,7 @@ git pull && poetry install && cd desktop && npm install && npm run build
248273
| v13.1 |**服务器部署+MySQL适配**(2026-03-15):腾讯云轻量Ubuntu服务器+阿里云RDS MySQL+Nginx直接部署+SSL |
249274
| v13.2 |**官网上线+扩展UI修复**(2026-03-17):HTTPS证书+下载页+启动按钮状态Bug修复+经验库自动fallback远程 |
250275
| v14.x |**插件UI大升级**(2026-03-17~22):<br>主题切换(浅色/深色,localStorage持久)<br>设置改浮动下拉菜单(不占Tab位)<br>齿轮按钮内联到引擎状态行(随页面滚动,不遮挡内容)<br>顽固Bug 4级递进系统(localStorage持久,通过后自动清零)<br>蓝本扫描排除test_fixtures/.venv/目录 |
276+
| v14.0-F |**登录UI迷你横条+TRAE修复**(2026-03-25):<br>登录面板改为单行状态条+折叠下拉(删除大 auth-panel)<br>注册→跳官网 openExternal(不内嵌注册表单)<br>retainContextWhenHidden:true 修复日志反复打印<br>TRAE旧publisher版本(.bak)清理,新vsix安装成功 |
251277
| v18 |**编程AI蓝本自动生成规则注入**(2026-03-20):AGENTS.md+copilot-instructions.md+全IDE规则文件映射 |
252278
| v19 |**页面指纹识别系统**(2026-03-21):dump UI树提取文本指纹→秒级识别当前页→复用坐标缓存。Android/桌面已实现,iOS待Mac环境实现 |
253279
| v20 |**批量蓝本测试结果修复+全平台审计**(2026-03-21):inBatchMode竞态保护+步骤蓝本标签+桌面持久化缓存+移动端溢出检测 |

0 commit comments

Comments
 (0)