Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,24 @@ pnpm run e2e:test

> For the full script list, see [`package.json`](package.json). For agent-specific commands, verification, and architecture rules, see [`AGENTS.md`](AGENTS.md).

### Desktop debugging tools

When working on desktop UI/UX, the `devtools` Cargo feature provides additional debugging capabilities. It is automatically enabled in `dev` builds and `release-fast` profile builds, but never in `release` builds for end users.

| Shortcut | Action |
|---|---|
| `Cmd/Ctrl + Shift + I` | Toggle element inspector — hover to highlight elements, click to capture metadata |
| `Cmd/Ctrl + Shift + J` | Open native webview DevTools window |

The element inspector injects a lightweight script into the main webview. When you click an element, it captures:
- Tag, id, class, CSS selector path
- Computed styles and CSS variables
- Box model (margin, padding, border)
- Color values (text, background, border)
- Element attributes

Captured data is logged as structured JSON under the `bitfun::devtools` target.

## Code Standards and Architecture Constraints

### Logging
Expand Down
18 changes: 18 additions & 0 deletions CONTRIBUTING_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,24 @@ pnpm run e2e:test

> 完整脚本列表见 [`package.json`](package.json)。agent 专用命令、验证与架构规则见 [`AGENTS.md`](AGENTS.md)。

### 桌面端调试工具

开发桌面端 UI/UX 时,`devtools` Cargo feature 提供额外的调试能力。它在 `dev` 构建和 `release-fast` profile 构建中自动启用,但在面向最终用户的 `release` 构建中永不启用。

| 快捷键 | 功能 |
|---|---|
| `Cmd/Ctrl + Shift + I` | 切换元素检查器 — 悬停高亮元素,点击采集元数据 |
| `Cmd/Ctrl + Shift + J` | 打开原生 webview DevTools 窗口 |

元素检查器向主 webview 注入一个轻量脚本。点击元素后会采集:
- 标签、id、class、CSS 选择器路径
- Computed styles 和 CSS 变量
- Box model(margin、padding、border)
- 颜色值(文本、背景、边框)
- 元素属性

采集的数据以结构化 JSON 形式输出到 `bitfun::devtools` 日志目标下。

## 代码规范与架构约束

### 日志规范
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@
"desktop:dev:raw": "cross-env-shell CI=true \"cd src/apps/desktop && tauri dev\"",
"desktop:build": "node scripts/desktop-tauri-build.mjs",
"desktop:build:fast": "node scripts/desktop-tauri-build.mjs --debug --no-bundle",
"desktop:build:release-fast": "node scripts/desktop-tauri-build.mjs --no-bundle -- --profile release-fast",
"desktop:build:release-fast": "node scripts/desktop-tauri-build.mjs --no-bundle -- --profile release-fast --features devtools",
"desktop:build:exe": "node scripts/desktop-tauri-build.mjs --no-bundle",
"desktop:build:nsis": "node scripts/desktop-tauri-build.mjs --bundles nsis",
"desktop:build:nsis:fast": "node scripts/desktop-tauri-build.mjs --bundles nsis -- --profile release-fast",
"desktop:build:nsis:fast": "node scripts/desktop-tauri-build.mjs --bundles nsis -- --profile release-fast --features devtools",
"desktop:build:arm64": "node scripts/desktop-tauri-build.mjs --target aarch64-apple-darwin --bundles dmg",
"desktop:build:x86_64": "node scripts/desktop-tauri-build.mjs --target x86_64-apple-darwin --bundles dmg",
"desktop:build:linux": "node scripts/desktop-tauri-build.mjs",
Expand Down
9 changes: 9 additions & 0 deletions src/apps/desktop/AGENTS-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ pnpm run desktop:build:fast

`release-fast` profile(`Cargo.toml`):继承 `release`,但关闭 LTO、`codegen-units` 提高到 16、启用增量编译。编译速度显著提升,代价是二进制体积增大和边际运行时性能下降。

## DevTools feature(模型规则)

`devtools` Cargo feature 用于桌面端 UI/UX 调试。添加或修改调试相关代码时:

- 所有调试专用 API 和 command 必须用 `#[cfg(any(debug_assertions, feature = "devtools"))]` 保护
- 在 `#[cfg(not(any(debug_assertions, feature = "devtools")))]` 下提供 no-op stub,确保 command 始终可以注册到 `invoke_handler`
- 该 feature 通过 `--features devtools` 在 `dev` 构建和 `release-fast` profile 构建中自动启用
- 面向最终用户的 `release` profile 构建中永不启用

## 验证

```bash
Expand Down
9 changes: 9 additions & 0 deletions src/apps/desktop/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ pnpm run desktop:build:fast

`release-fast` profile (`Cargo.toml`): inherits `release` but disables LTO, increases `codegen-units` to 16, enables incremental compilation. Significantly faster at the cost of binary size and marginal runtime performance.

## DevTools feature (model rule)

The `devtools` Cargo feature exists for debugging UI/UX in the desktop app. When adding or modifying debug-related code:

- Guard all debug-only APIs and commands with `#[cfg(any(debug_assertions, feature = "devtools"))]`
- Provide no-op stubs under `#[cfg(not(any(debug_assertions, feature = "devtools")))]` so commands can always be registered in `invoke_handler`
- The feature is enabled automatically in `dev` builds and `release-fast` profile builds via `--features devtools`
- Never enable in `release` profile builds intended for end users

## Verification

```bash
Expand Down
7 changes: 7 additions & 0 deletions src/apps/desktop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ windows = { version = "0.61.3", features = [
] }
windows-core = "0.61.2"

[features]
default = []
# Enable webview devtools and element inspector for development builds.
# Only active in debug builds or when explicitly requested via --features devtools.
# Never enabled in release profile intended for end users.
devtools = ["tauri/devtools"]

[target.'cfg(target_os = "linux")'.dependencies]
atspi = "0.29"
leptess = "0.14.0"
111 changes: 111 additions & 0 deletions src/apps/desktop/src/api/debug_api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//! Debug API for desktop development.
//!
//! Provides element inspector, devtools control, and screenshot debugging.
//!
//! # Compilation guards
//! All public items in this module are guarded by `#[cfg(any(debug_assertions, feature = "devtools"))]`.
//! This ensures zero debug code is compiled into release builds intended for end users.

use serde::Deserialize;

#[cfg(any(debug_assertions, feature = "devtools"))]
use tauri::Manager;

// ---------------------------------------------------------------------------
// Request / Response types
// ---------------------------------------------------------------------------

/// Payload sent by the injected inspector script when user clicks an element.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DebugElementPickedRequest {
pub tag_name: String,
pub path: String,
pub id: Option<String>,
pub class_name: Option<String>,
pub text_content: String,
pub outer_html: String,
pub computed_styles: serde_json::Value,
pub css_variables: serde_json::Value,
pub color_info: serde_json::Value,
pub box_model: serde_json::Value,
pub attributes: serde_json::Value,
}

// ---------------------------------------------------------------------------
// Commands
// ---------------------------------------------------------------------------

/// Called by the injected inspector script when user clicks an element.
///
/// Logs the full element information as structured JSON so developers can
/// inspect tag, classes, computed styles, colors, box-model, etc.
#[tauri::command]
#[cfg(any(debug_assertions, feature = "devtools"))]
pub async fn debug_element_picked(request: DebugElementPickedRequest) -> Result<(), String> {
let payload = serde_json::json!({
"tag_name": request.tag_name,
"path": request.path,
"id": request.id,
"class_name": request.class_name,
"text_content": request.text_content,
"outer_html_preview": request.outer_html,
"computed_styles": request.computed_styles,
"css_variables": request.css_variables,
"color_info": request.color_info,
"box_model": request.box_model,
"attributes": request.attributes,
});

log::info!(
target: "bitfun::devtools",
"Element picked: {}",
serde_json::to_string_pretty(&payload).unwrap_or_default()
);

Ok(())
}

/// Open the native webview DevTools window for the main window.
#[tauri::command]
#[cfg(any(debug_assertions, feature = "devtools"))]
pub async fn debug_open_devtools(app: tauri::AppHandle) -> Result<(), String> {
let window = app
.get_webview_window("main")
.ok_or("Main window not found")?;
window.open_devtools();
Ok(())
}

/// Close the native webview DevTools window for the main window.
#[tauri::command]
#[cfg(any(debug_assertions, feature = "devtools"))]
pub async fn debug_close_devtools(app: tauri::AppHandle) -> Result<(), String> {
let window = app
.get_webview_window("main")
.ok_or("Main window not found")?;
window.close_devtools();
Ok(())
}

// ---------------------------------------------------------------------------
// No-op stubs for release builds (so the module always compiles)
// ---------------------------------------------------------------------------

#[tauri::command]
#[cfg(not(any(debug_assertions, feature = "devtools")))]
pub async fn debug_element_picked(_request: DebugElementPickedRequest) -> Result<(), String> {
Err("DevTools not available in release builds".to_string())
}

#[tauri::command]
#[cfg(not(any(debug_assertions, feature = "devtools")))]
pub async fn debug_open_devtools(_app: tauri::AppHandle) -> Result<(), String> {
Err("DevTools not available in release builds".to_string())
}

#[tauri::command]
#[cfg(not(any(debug_assertions, feature = "devtools")))]
pub async fn debug_close_devtools(_app: tauri::AppHandle) -> Result<(), String> {
Err("DevTools not available in release builds".to_string())
}
1 change: 1 addition & 0 deletions src/apps/desktop/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod computer_use_api;
pub mod config_api;
pub mod context_upload_api;
pub mod cron_api;
pub mod debug_api;
pub mod diff_api;
pub mod dto;
pub mod editor_ai_api;
Expand Down
4 changes: 4 additions & 0 deletions src/apps/desktop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,10 @@ pub async fn run() {
api::announcement_api::never_show_announcement,
api::announcement_api::trigger_announcement,
api::announcement_api::get_announcement_tips,
// Debug API (no-op stubs in release builds)
api::debug_api::debug_element_picked,
api::debug_api::debug_open_devtools,
api::debug_api::debug_close_devtools,
])
.run(tauri::generate_context!());
if let Err(e) = run_result {
Expand Down
6 changes: 3 additions & 3 deletions src/apps/desktop/src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,17 +278,17 @@ pub fn create_main_window(app_handle: &tauri::AppHandle) {

match builder.build() {
Ok(window) => {
#[cfg(debug_assertions)]
#[cfg(any(debug_assertions, feature = "devtools"))]
{
if std::env::var("BITFUN_OPEN_DEVTOOLS")
.map(|v| v == "1")
.unwrap_or(false)
{
window.open_devtools();
let _ = window.open_devtools();
}
}

#[cfg(not(debug_assertions))]
#[cfg(not(any(debug_assertions, feature = "devtools")))]
let _ = window;
}
Err(e) => {
Expand Down
4 changes: 4 additions & 0 deletions src/web-ui/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { createLogger } from '@/shared/utils/logger';
import { useWorkspaceContext } from '../infrastructure/contexts/WorkspaceContext';
import SplashScreen from './components/SplashScreen/SplashScreen';
import { useGlobalSceneShortcuts } from './hooks/useGlobalSceneShortcuts';
import { useDebugInspector } from '@/infrastructure/debug/useDebugInspector';

// Toolbar Mode
import { ToolbarModeProvider } from '../flow_chat';
Expand Down Expand Up @@ -189,6 +190,9 @@ function App() {
// Top SceneBar: Mod+Alt+1..9 / Mod+Alt+PageUp/PageDown
useGlobalSceneShortcuts();

// Debug inspector shortcuts (desktop devtools only)
useDebugInspector();

// Unified layout via a single AppLayout
return (
<ChatProvider>
Expand Down
4 changes: 4 additions & 0 deletions src/web-ui/src/flow_chat/components/FlowTextBlock.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
margin: 0 0 0.6rem 0;
font-size: var(--flowchat-font-size-base);
line-height: 1.65;
/* Transparent border to match the box-sizing of bordered cards (BaseToolCard,
* TerminalRegion, etc.) so that text and card content share the same left edge. */
border: 1px solid transparent;
box-sizing: border-box;

/*
* Markdown rendering inside a flow text block:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,68 @@
}
}

// Reviewer context section replaces raw prompt for review-team members.
&__reviewer-section {
background: transparent;
flex-shrink: 0;

summary {
padding: 8px 12px;
font-size: 12px;
font-weight: 500;
color: var(--color-text-secondary);
cursor: pointer;
user-select: none;
transition: all 0.15s ease;
border: 1px dashed var(--tool-card-border, rgba(255, 255, 255, 0.15));
border-radius: 6px;

&:hover {
color: var(--color-text-primary);
}

&::marker {
color: var(--color-text-muted);
}
}

&[open] summary {
border-radius: 6px 6px 0 0;
border-bottom: none;
}
}

&__reviewer-context {
display: flex;
flex-direction: column;
gap: 6px;
padding: 10px 12px;
border: 1px dashed var(--tool-card-border, rgba(255, 255, 255, 0.15));
border-top: none;
border-radius: 0 0 6px 6px;
font-size: 12px;
line-height: 1.5;
}

&__reviewer-role {
font-weight: 600;
font-size: 12px;
}

&__reviewer-desc {
color: var(--color-text-muted);
}

&__reviewer-responsibilities {
margin: 0;
padding-left: 16px;
list-style: disc;

li {
color: var(--color-text-secondary);
}
}

&__execution {
display: flex;
flex-direction: column;
Expand Down
Loading
Loading