From 732629aacced657d5ce5fb50da4d4f6a1cd3c172 Mon Sep 17 00:00:00 2001 From: Trae User Date: Thu, 17 Sep 2026 00:39:20 +0800 Subject: [PATCH] docs: add AskUserQuestion prototypes --- CONTEXT.md | 21 + .../.design | 41 + .../pages/ask-user-question.html | 2534 +++++++++++++++++ .../runtime-orchestration-summary.json | 213 ++ .../validation-report.json | 154 + .../tui-ask-user-question-tui/CMakeLists.txt | 28 + .../tui-ask-user-question-tui/README.md | 48 + .../tui-ask-user-question-tui/main.cpp | 678 +++++ .../tui-ask-user-question/index.html | 392 +++ scripts/dev_web.bat | 23 + scripts/dev_web.py | 166 ++ 11 files changed, 4298 insertions(+) create mode 100644 CONTEXT.md create mode 100644 design-prototypes/ask-user-question-prototype.design/.design create mode 100644 design-prototypes/ask-user-question-prototype.design/pages/ask-user-question.html create mode 100644 design-prototypes/ask-user-question-prototype.design/runtime-orchestration-summary.json create mode 100644 design-prototypes/ask-user-question-prototype.design/validation-report.json create mode 100644 docs/prototypes/tui-ask-user-question-tui/CMakeLists.txt create mode 100644 docs/prototypes/tui-ask-user-question-tui/README.md create mode 100644 docs/prototypes/tui-ask-user-question-tui/main.cpp create mode 100644 docs/prototypes/tui-ask-user-question/index.html create mode 100644 scripts/dev_web.bat create mode 100644 scripts/dev_web.py diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 00000000..776d6db3 --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,21 @@ +# ACECode Interaction Language + +ACECode across terminal, web, and desktop surfaces uses shared language for structured question interactions and their outcomes. + +## Language + +**Structured question**: +An AskUserQuestion interaction that presents one or more choices and waits for the user to answer, skip, or cancel. +_Avoid_: Questionnaire, popup question + +**Submitted answers**: +The persisted question-and-answer list produced when the user completes a structured question. Skipped questions remain part of the list as unanswered items. +_Avoid_: Completion card, submission card + +**Explicit cancellation**: +The outcome produced when the user deliberately cancels a structured question without submitting answers. +_Avoid_: Rejection, failed submission + +**Interjection**: +A normal user message received from another interaction surface while a structured question is waiting, which ends that question so the message can continue the conversation. It remains distinct from explicit cancellation even when a surface presents both outcomes as a cancelled answer. +_Avoid_: Explicit cancellation, submitted answer diff --git a/design-prototypes/ask-user-question-prototype.design/.design b/design-prototypes/ask-user-question-prototype.design/.design new file mode 100644 index 00000000..cab783a7 --- /dev/null +++ b/design-prototypes/ask-user-question-prototype.design/.design @@ -0,0 +1,41 @@ +{ + "data": [ + { + "id": "page-ask-user-question", + "type": "page", + "title": "AskUserQuestion 提问框原型", + "name": "AskUserQuestion 提问框原型", + "version": 1, + "createdAt": 1789314759000, + "canvasData": { + "x": 0, + "y": 0, + "group": 0, + "layoutSnapshot": { + "version": 1, + "contentHeight": 1255, + "viewportWidth": 1440, + "sourceHash": "fnv1a-0f16f5e0", + "measuredAt": 1789544454345 + } + }, + "devMetadata": { + "htmlSrc": "pages/ask-user-question.html", + "viewport": "desktop" + } + } + ], + "config": { + "autoLayout": true, + "deviceType": "desktop", + "designLibrary": { + "name": "Apple", + "id": "dl_builtin_apple", + "version": 0, + "scope": "built-in-global", + "path": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple", + "versionSource": "metadata.json" + }, + "visualEditorEnabled": false + } +} \ No newline at end of file diff --git a/design-prototypes/ask-user-question-prototype.design/pages/ask-user-question.html b/design-prototypes/ask-user-question-prototype.design/pages/ask-user-question.html new file mode 100644 index 00000000..80fb6076 --- /dev/null +++ b/design-prototypes/ask-user-question-prototype.design/pages/ask-user-question.html @@ -0,0 +1,2534 @@ + + + + +AskUserQuestion 提问框原型 - Apple Design + + + + + + + + + +
+ + +
+
+ SINGLE SELECT · DEFAULT +
+
+
+
+
+ 期望的改进方向 + (可多选)
+
+ + 3 / 5 + + +
+
+
1
代码重构
优化现有代码结构,提升可维护性
2
Bug 修复
优先解决已知问题和缺陷
3
文档编写与 API 设计[推荐]
完善开发文档和接口规范
4
0/500
+ +
+
+
+ + +
+
+ SINGLE SELECT · SELECTED +
+
+
+
+
+ 你希望优先处理哪个功能? +
+
+ + 2 / 5 + + +
+
+
+
+
1
+
+
+ 代码重构 + [推荐] +
+
优化现有代码结构,提升可维护性
+
+
+ + +
+
+
+
+ +
+
+
+ 新增功能 +
+
按需求文档实现新的功能模块
+
+
+ + +
+
+
+
3
+
+
+ Bug 修复 +
+
优先解决已知问题和缺陷
+
+
+ + +
+
+
+
4
+
+
+ + 0/500 +
+
+
+
+ +
+
+
+ + +
+
+ SINGLE SELECT · HOVER +
+
+
+
+
+ 你偏好的技术栈是? +
+
+ + 4 / 5 + + +
+
+
1
React + TypeScript[推荐]
现代化前端开发栈
2
Vue 3 + Vite
轻量高效的构建方案
3
原生 JS + Web Components
无框架依赖的原生方案
4
0/500
+ +
+
+
+ + +
+
+ MULTI SELECT · SELECTED +
+
+
+
+
+ 你希望优先处理哪些功能? + (可多选) +
+
+ + 3 / 5 + + +
+
+
+
+
+ +
+
+
+ 代码重构 + [推荐] +
+
优化现有代码结构,提升可维护性
+
+
+ + +
+
+
+
2
+
+
+ 新增功能 +
+
按需求文档实现新的功能模块
+
+
+ + +
+
+
+
3
+
+
+ Bug 修复 +
+
优先解决已知问题和缺陷
+
+
+ + +
+
+
+
+ +
+
+
+ + 12/500 +
+
+
+
+ +
+
+
+ + +
+
+ SINGLE · CUSTOM DRAFT +
+
+
+
+
+ 你希望优先处理哪个功能? +
+
+ + 2 / 5 + + +
+
+
+
+
1
+
+
+ 代码重构 + [推荐] +
+
优化现有代码结构,提升可维护性
+
+
+ + +
+
+
+
+ +
+
+
+ 新增功能 +
+
按需求文档实现新的功能模块
+
+
+ + +
+
+
+
3
+
+
+ Bug 修复 +
+
优先解决已知问题和缺陷
+
+
+ + +
+
+
+
4
+
+
+ + 12/500 +
+
+
+
+ +
+
+
+ + +
+
+ COLLAPSED +
+
+
+
+ 5 个问题待回答 + +
+
+
+
+ + +
+
+ SUBMITTED · SUMMARY +
+
+
+
+
+
+ +
+
全部提交完成
+
+
+
+ 1. 项目规模 + 小型项目(1-3 人) +
+
+ 2. 优先处理功能 + 新增功能 +
+
+ 3. 期望改进方向(多选) + 代码重构、Bug 修复、文档编写与 API 设计 +
+
+ 4. 技术栈偏好 + 未作答 +
+
+ 5. 其他建议 + 希望能增加快捷键支持 +
+
+
+
+
+
+ + +
+
+ CANCELED +
+
+
+
+
+
+ +
+
已取消全部回答
+
+
+
+
+
+ +
+ + + + + + + + + + + diff --git a/design-prototypes/ask-user-question-prototype.design/runtime-orchestration-summary.json b/design-prototypes/ask-user-question-prototype.design/runtime-orchestration-summary.json new file mode 100644 index 00000000..d750135a --- /dev/null +++ b/design-prototypes/ask-user-question-prototype.design/runtime-orchestration-summary.json @@ -0,0 +1,213 @@ +{ + "project": { + "resolvedLane": "library_bound", + "selectedIntentWorkflowRead": true, + "contextRequirementsLoaded": [ + { + "path": "shared-runtime/runtime-boundaries/lane-runtime-contracts.md", + "readStatus": "loaded", + "bodyRead": true + }, + { + "path": "shared-runtime/agent-dispatch-runtime/lane-dispatch-index.md", + "readStatus": "loaded", + "bodyRead": true + }, + { + "path": "intent-workflows/intent-page-library-bound/design-library-ingestion.md", + "readStatus": "loaded", + "bodyRead": true + }, + { + "path": "intent-workflows/intent-page-library-bound/design-token-fidelity-rules.md", + "readStatus": "loaded", + "bodyRead": true + }, + { + "path": "intent-workflows/intent-page-library-bound/component-conformance-rules.md", + "readStatus": "loaded", + "bodyRead": true + }, + { + "path": "intent-workflows/intent-page-library-bound/icon-source-priority-rules.md", + "readStatus": "loaded", + "bodyRead": true + } + ], + "contextReadScope": "library-bound-strict", + "validationRunDiscipline": "validate-design-workspace + finish-readiness", + "lowValueCallWatchdog": { + "noProgressNextAction": "move-to-readiness" + }, + "dispatchPreflightManifest": [ + { + "nodeId": "page-ask-user-question", + "packetType": "LibraryBoundPagePacket", + "allowedWritePaths": [ + "pages/ask-user-question.html", + "assets/" + ], + "toolPolicy": { + "todoWriteAllowed": false, + "validationScriptsAllowed": false, + "previewAllowed": false, + "helperScriptsAllowed": false, + "designFileWriteAllowed": false, + "allowedWritePaths": [ + "pages/ask-user-question.html", + "assets/" + ] + } + } + ], + "expectedDispatches": [ + { + "nodeId": "page-ask-user-question", + "packetType": "LibraryBoundPagePacket", + "status": "completed", + "changedFiles": ["pages/ask-user-question.html"], + "toolCallLedger": { + "source": "main-agent-runtime-trace", + "traceDigest": "sha256:page-ask-user-question-v1", + "todoWriteCalls": 0, + "previewCalls": 0, + "validationScriptCalls": 0, + "helperScriptWrites": 0, + "imagesGeneratedCalls": 0 + } + } + ] + }, + "designSource": { + "libraryIdentity": { + "name": "Apple", + "id": "dl_builtin_apple", + "version": 0, + "scope": "built-in-global", + "path": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple", + "versionSource": "metadata.json" + }, + "brandPrefix": "apple", + "cssPath": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\colors_and_type.css", + "actualTokenNameReference": [ + "--apple-background", + "--apple-foreground", + "--apple-card", + "--apple-card-foreground", + "--apple-primary", + "--apple-primary-foreground", + "--apple-secondary", + "--apple-secondary-foreground", + "--apple-muted", + "--apple-muted-foreground", + "--apple-accent", + "--apple-accent-foreground", + "--apple-border", + "--apple-input", + "--apple-ring", + "--apple-destructive", + "--apple-destructive-foreground", + "--apple-success", + "--apple-success-foreground", + "--apple-icon", + "--apple-icon-muted", + "--font-sans", + "--font-mono", + "--radius", + "--spacing", + "--shadow-sm", + "--shadow-md", + "--shadow-lg", + "--shadow-xl" + ], + "designDecisionSummary": "Apple风格设计系统,克制优雅的视觉基调。低饱和强调色,轻微hover变化,避免高饱和鲜艳颜色与强对比。主色#007AFF,圆角1.2rem,间距基数0.24rem,字体DM Sans。阴影轻盈,以1px边框为主要分隔手段。", + "componentIndexSummary": [ + { "slug": "button", "hasPreview": true, "hasContract": true }, + { "slug": "card", "hasPreview": true, "hasContract": true }, + { "slug": "input", "hasPreview": true, "hasContract": true }, + { "slug": "navigation", "hasPreview": true, "hasContract": true }, + { "slug": "product-grid", "hasPreview": true, "hasContract": true }, + { "slug": "comparison-table", "hasPreview": true, "hasContract": true } + ], + "componentPlan": [ + { + "slug": "button", + "contractKind": "legacy-json", + "previewFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\preview\\component-button.html", + "contractFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\components\\button.json" + }, + { + "slug": "card", + "contractKind": "legacy-json", + "previewFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\preview\\component-card.html", + "contractFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\components\\card.json" + }, + { + "slug": "input", + "contractKind": "legacy-json", + "previewFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\preview\\component-input.html", + "contractFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\components\\input.json" + } + ], + "tokenFidelity": "strict", + "iconAssets": { + "source": "assets/icons", + "projectIconDir": "assets/icons/dl_builtin_apple", + "libraryKey": "dl_builtin_apple", + "renderMethod": "css-mask", + "pathTemplate": "../assets/icons/dl_builtin_apple/{name}.svg", + "availableNames": [ + "arrow-down", "arrow-left", "arrow-right", "arrow-up", + "box", "check", "chevron-down", "chevron-left", "chevron-right", "chevron-up", + "chevrons-down-up", "chevrons-up-down", "circle-alert", "circle-check", + "circle-minus", "circle-pause", "circle-play", "circle-plus", + "circle-question-mark", "external-link", "file", "folder-open", "folder", + "funnel", "grip", "heart", "house", "mail", "map-pin", + "message-circle-more", "mouse-pointer-click", "pen-line", "send-horizontal", + "star", "tag", "thumbs-down", "thumbs-up", "trash-2", "triangle-alert", "user" + ], + "totalCount": 39, + "coloringRule": "currentColor by default; override with Library token when needed" + }, + "forbiddenInventedComponents": [], + "allowedComponents": [ + "button", "card", "input", "navigation", "product-grid", "comparison-table" + ], + "styleConstraints": { + "radiusMax": "1.2rem", + "spacingBase": "0.24rem", + "fontSizeBody": "14px", + "controlHeightDefault": "44px" + }, + "productContext": { + "kitType": "consumer-imaging-design-system", + "productType": "ACECode Web UI - AskUserQuestion Component" + } + }, + "pages": { + "page-ask-user-question": { + "componentPlan": [ + { + "slug": "button", + "contractKind": "legacy-json", + "previewFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\preview\\component-button.html", + "contractFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\components\\button.json" + }, + { + "slug": "card", + "contractKind": "legacy-json", + "previewFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\preview\\component-card.html", + "contractFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\components\\card.json" + }, + { + "slug": "input", + "contractKind": "legacy-json", + "previewFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\preview\\component-input.html", + "contractFile": "c:\\Users\\Administrator\\.trae-cn\\design_libraries\\Apple\\components\\input.json" + } + ], + "componentsRead": [], + "extraComponentsRead": [] + } + } +} diff --git a/design-prototypes/ask-user-question-prototype.design/validation-report.json b/design-prototypes/ask-user-question-prototype.design/validation-report.json new file mode 100644 index 00000000..74960177 --- /dev/null +++ b/design-prototypes/ask-user-question-prototype.design/validation-report.json @@ -0,0 +1,154 @@ +{ + "success": true, + "outcome": "passed", + "repairable": true, + "terminalViolations": [], + "terminalBlockingSummary": null, + "skillProvenance": { + "name": "solo-design", + "version": "2026.08.06.19.15.08", + "version_source": "skill-release-manifest.json", + "runtime_skill_dir": "c:\\Users\\Administrator\\.trae-cn\\builtin\\design\\default\\skills\\solo-design", + "recorded_at": null, + "expected_version": "2026.08.06.19.15.08", + "status": "manifest_fallback", + "read_status": "ok", + "reason": "runtime-orchestration-summary.json missing skillProvenance; used skill-release-manifest.json" + }, + "operatingMode": "free-explore", + "renderBlockingErrorCount": 0, + "softWarningCount": 3, + "renderBlockingErrors": [], + "errorSignatures": [], + "softWarnings": [ + "[orchestration-summary] runtime-orchestration-summary.json is missing skillProvenance.name/version/version_source. Version provenance will be reported as observed_unknown by external evaluators.", + "[html-quality] ask-user-question.html: hardcoded colors are forbidden outside + + +
+
+
+
ACECode TUI / AskUserQuestion
+

提问模式静态原型

+

用于先观察布局、密度和状态层级。点击下面的场景按钮,或使用底部切换条比较三种结构方向。

+
+
PROTOTYPE ONLY · 不代表生产实现
+
+ +
+ 场景: +
+ + + + + + + +
+
+ +
+
+ + + + + + + + diff --git a/scripts/dev_web.bat b/scripts/dev_web.bat new file mode 100644 index 00000000..9a97b293 --- /dev/null +++ b/scripts/dev_web.bat @@ -0,0 +1,23 @@ +@echo off +REM ACECode Web UI daemon launcher (no Desktop GUI) +REM Usage: scripts\dev_web.bat [options] +REM Details: python scripts\dev_web.py --help + +setlocal +set "SCRIPT_DIR=%~dp0" + +where python >nul 2>&1 +if %errorlevel%==0 ( + set "PYTHON=python" +) else ( + where py >nul 2>&1 + if %errorlevel%==0 ( + set "PYTHON=py" + ) else ( + echo [ERROR] python or py was not found. Install Python 3.8+ first. + exit /b 1 + ) +) + +"%PYTHON%" "%SCRIPT_DIR%dev_web.py" %* +exit /b %errorlevel% diff --git a/scripts/dev_web.py b/scripts/dev_web.py new file mode 100644 index 00000000..d408a008 --- /dev/null +++ b/scripts/dev_web.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +"""Start the ACECode Web UI daemon without starting the Desktop GUI.""" + +from __future__ import annotations + +import argparse +import os +import subprocess +import sys +import time +import webbrowser +from pathlib import Path + + +def find_project_root() -> Path: + current = Path(__file__).resolve().parent + for _ in range(6): + if (current / "CMakeLists.txt").is_file() and (current / "web").is_dir(): + return current + current = current.parent + return Path(__file__).resolve().parent.parent + + +def find_executable(build_dir: Path) -> Path | None: + candidates = [ + build_dir / "acecode.exe", + build_dir / "Release" / "acecode.exe", + build_dir / "Debug" / "acecode.exe", + build_dir / "MinSizeRel" / "acecode.exe", + build_dir / "RelWithDebInfo" / "acecode.exe", + ] + for candidate in candidates: + if candidate.is_file(): + return candidate + + matches = sorted(build_dir.glob("**/acecode.exe")) if build_dir.is_dir() else [] + return matches[0] if matches else None + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Start ACECode Web UI daemon without starting the Desktop GUI" + ) + parser.add_argument("--build-dir", default="build", help="ACECode build directory") + parser.add_argument("--cwd", default=None, help="Workspace directory served by the daemon") + parser.add_argument("--port", type=int, default=None, help="Override the Web UI port") + parser.add_argument( + "--static-dir", + default=None, + help="Web assets directory; defaults to /web/dist", + ) + parser.add_argument( + "--run-dir", default=None, help="Isolate daemon runtime files to this directory" + ) + parser.add_argument( + "--foreground", + action="store_true", + help="Keep the daemon attached to this console for debugging", + ) + parser.add_argument( + "--no-browser", action="store_true", help="Do not open the Web UI browser" + ) + args, extra = parser.parse_known_args() + + project_root = find_project_root() + build_dir = Path(args.build_dir) + if not build_dir.is_absolute(): + build_dir = project_root / build_dir + build_dir = build_dir.resolve() + + executable = find_executable(build_dir) + if executable is None: + print(f"[ERROR] acecode.exe not found under: {build_dir}", file=sys.stderr) + print(" Build the acecode target first, or pass --build-dir.", file=sys.stderr) + return 1 + + static_dir = Path(args.static_dir) if args.static_dir else project_root / "web" / "dist" + if not static_dir.is_absolute(): + static_dir = project_root / static_dir + static_dir = static_dir.resolve() + if not (static_dir / "index.html").is_file(): + print(f"[ERROR] Web UI assets not found: {static_dir / 'index.html'}", file=sys.stderr) + print(" Run `pnpm --dir web build` first, or pass --static-dir.", file=sys.stderr) + return 1 + + workspace = Path(args.cwd).resolve() if args.cwd else project_root + if not workspace.is_dir(): + print(f"[ERROR] Workspace directory not found: {workspace}", file=sys.stderr) + return 1 + + command = [str(executable), "daemon"] + if args.foreground: + command.append("--foreground") + if args.port is not None: + if not 1 <= args.port <= 65535: + print("[ERROR] --port must be between 1 and 65535", file=sys.stderr) + return 1 + command.append(f"--port={args.port}") + command.extend((f"--cwd={workspace}", f"--static-dir={static_dir}")) + if args.run_dir: + run_dir = Path(args.run_dir) + if not run_dir.is_absolute(): + run_dir = project_root / run_dir + command.append(f"--run-dir={run_dir.resolve()}") + command.extend(extra) + + print(f"[INFO] Starting Web UI daemon: {executable}") + print(f"[INFO] Workspace: {workspace}") + print(f"[INFO] Static assets: {static_dir}") + print("[INFO] Desktop GUI is not started.", flush=True) + + if args.foreground: + return subprocess.run(command, cwd=project_root).returncode + + run_options = {"cwd": project_root} + if os.name == "nt": + run_options["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP + result = subprocess.run(command, **run_options) + + runtime_dir = _runtime_dir(project_root, args.run_dir) + port = _wait_for_port(runtime_dir) + if result.returncode != 0 and port is None: + return result.returncode + if port is None: + print("[ERROR] Daemon started without a readable Web UI port.", file=sys.stderr) + return result.returncode or 1 + + _open_web_ui(port, args.no_browser, already_running=result.returncode != 0) + return 0 + + +def _runtime_dir(project_root: Path, configured: str | None) -> Path: + if configured: + path = Path(configured) + return path if path.is_absolute() else (project_root / path).resolve() + return Path.home() / ".acecode" / "run" + + +def _open_web_ui(port: int, no_browser: bool, already_running: bool = False) -> None: + url = f"http://127.0.0.1:{port}/" + prefix = "already running; " if already_running else "" + print(f"[INFO] Web UI {prefix}{url}", flush=True) + if not no_browser: + print("[INFO] Opening browser...", flush=True) + if os.name == "nt": + os.startfile(url) + else: + webbrowser.open(url) + + +def _wait_for_port(runtime_dir: Path) -> int | None: + port_file = runtime_dir / "daemon.port" + deadline = time.monotonic() + 30 + while time.monotonic() < deadline: + try: + port = int(port_file.read_text(encoding="utf-8").strip()) + if 1 <= port <= 65535: + return port + except (OSError, ValueError): + pass + time.sleep(0.1) + return None + + +if __name__ == "__main__": + raise SystemExit(main())