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
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

- Read README.md, docs/PLAN.md, docs/FEATURES.md and the active milestone before making architecture decisions.
- Follow docs/adr/0006-confirmed-project-direction.md: fully recreate WindTerm 2.7.0, with Windows 11 x64 as the first complete-version target and Windows/Linux/macOS as the final platform goal. Prioritize terminal foundations and performance before SSH/SFTP and the remaining features.
- Preserve Alacritty as a candidate, not an approved product-core selection. Keep investigating the original code and mechanisms; internal implementation need not be identical.
- Follow docs/adr/0007-bundled-microsoft-conpty.md: the confirmed Windows local-shell product route bundles a fixed Microsoft ConPTY. Consolidate existing integration into this route; system ConPTY and WinPTY are diagnostic references rather than parallel product routes. Core and GUI choices remain unconfirmed, and route confirmation is not implementation acceptance.
- Follow docs/adr/0008-cpp-core-qt-widgets.md: the confirmed product core is independently developed C++, and Qt Widgets is the current GUI route. Keep investigating the original code and mechanisms; internal implementation need not be identical. Preserve Alacritty code and evidence for reference/differential work rather than expanding it as the product core.
- Follow docs/adr/0007-bundled-microsoft-conpty.md: the confirmed Windows local-shell product route bundles a fixed Microsoft ConPTY. Consolidate existing integration into this route; system ConPTY and WinPTY are diagnostic references rather than parallel product routes. Route confirmation is not implementation acceptance.
- Default to observed WindTerm 2.7.0 behavior when it conflicts with standards, third-party behavior or existing project policy. Unknown behavior requires investigation; important deviations and original-bug fixes require user confirmation.
- The user confirms scope changes, major architecture/dependency choices, acceptance criteria and important behavior differences. Agents may autonomously research, experiment, implement, test and fix within confirmed scope. Record proposal/confirmed status and confirmation evidence in ADRs and issues; merged code or closed issues do not imply user approval.
- Performance tolerances have no approved numeric values yet. Propose them from reliable baseline evidence and explicitly agree them with the user before using them for acceptance; keep existing diagnostic guards separate.
- The project has a limited Windows local-shell prototype. Do not claim a daily-use release, Linux/macOS client, complete WindTerm parity or measured performance equivalence.
- Do not lock the stack or add a large application skeleton before benchmark evidence supports it.
- Implement the user-confirmed route in small, measurable slices. Do not add a large application skeleton or claim that the selected stack meets performance targets before benchmark evidence supports it.
- Every performance claim needs workload identity, environment, correctness checks, raw samples and limitations.
- Separate observed facts, hypotheses and design decisions. Static symbols do not prove runtime behavior.
- Keep parser/screen state, history, transport and presentation independently testable in later implementation.
Expand All @@ -20,8 +20,8 @@
- Follow docs/adr/0002-automation-first-investigation.md: prefer unattended CLI/test workflows; do not turn automation blockers into repeated user clicks, commands or per-run confirmations. Preserve unresolved evidence gaps and continue independent investigation/prototype work.
- R1–R5 in docs/PLAN.md are the current product milestones. Legacy M0–M4 and G1–G3 names appear only in historical evidence. Investigation and independently testable implementation may proceed in parallel; a complete baseline is required for supported performance claims.
- docs/FEATURES.md owns current capabilities and limits; docs/PLAN.md owns milestone scope; the active milestone owns acceptance contracts; GitHub issues own execution status and blockers. Do not append batch diaries to current planning documents.
- Keep build configurations separate in claims: default core/system backend, main candidate/system backend, and an explicit experimental backend need their own source and validation identity. Do not combine their independent successes into a joint acceptance result.
- Keep build configurations separate in claims: the C++/Qt/fixed-ConPTY product route, legacy default/system build, Alacritty reference build and earlier backend experiments need their own source and validation identity. Do not combine their independent successes into a joint acceptance result or transfer candidate capabilities to the C++ core.
- Source comments explain responsibilities, invariants and policy. Put changing status, batch names and future work in the owning issue; retain fixed experiment/protocol identities when they define behavior or evidence. A TODO must point to a current task rather than duplicate the roadmap.
- Start only an issue whose real blockers are resolved. Record source observations separately from TermForge policies, implement independently, and validate the complete user-visible behavior. An unknown target behavior is an investigation item, not a passed comparison.
- ADR-0003's historical external-core comparison remains evidence for continued candidate work, not user confirmation of a permanent route. Before final product core/backend/GUI selection, revisit ADR-0003/0004 under ADR-0006 and obtain the user's confirmation of a concrete evidence-backed proposal. Do not silently turn experimental choices into permanent architecture.
- ADR-0003/0004 preserve historical comparison and Win32 presentation evidence; ADR-0007/0008 record the current user-confirmed routes. Substantial replacements, new major dependencies or important behavior deviations need a concrete evidence-backed proposal and user confirmation. Existing experimental success does not establish product acceptance.
- Documentation is primarily Chinese; technical identifiers and concise English summaries are welcome.
58 changes: 58 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.20)
project(termforge_experiments VERSION 0.1.0 LANGUAGES CXX)

option(TERMFORGE_BUILD_CANDIDATE_ADAPTER "Build the Rust candidate C++ adapter checks" OFF)
option(TERMFORGE_BUILD_QT_SHELL "Build the C++/Qt local-shell migration entry" OFF)

# Single-config generators use bin/, multi-config generators use bin/<config>/.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
Expand Down Expand Up @@ -327,3 +328,60 @@ if(BUILD_TESTING)
termforge_add_python_test(research_guards -m unittest research.tests.test_ui_probe research.tests.test_ui_typography research.tests.test_ui_r1 research.tests.test_ime_evidence -v)
endif()
endif()

if(TERMFORGE_BUILD_QT_SHELL)
if(NOT WIN32)
message(FATAL_ERROR "The first Qt local-shell migration entry requires Windows x64")
endif()
find_package(Qt6 6.10.3 EXACT REQUIRED COMPONENTS Widgets)
include(cmake/ConptyBackend.cmake)
get_target_property(qt_core_dll Qt6::Core IMPORTED_LOCATION_RELEASE)
get_filename_component(qt_binary_directory "${qt_core_dll}" DIRECTORY)
find_program(qt_deploy_tool windeployqt HINTS "${qt_binary_directory}" NO_DEFAULT_PATH REQUIRED)

function(termforge_qt_entry target)
target_link_libraries(${target} PRIVATE termforge_core Qt6::Widgets user32)
target_compile_definitions(${target} PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN
_WIN32_WINNT=0x0A00 QT_NO_KEYWORDS)
termforge_warnings(${target})
termforge_use_bundled_conpty(${target})
add_custom_target(${target}_font_bundle
COMMAND "${CMAKE_COMMAND}" -E make_directory "$<TARGET_FILE_DIR:${target}>/assets/fonts"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${PROJECT_SOURCE_DIR}/assets/fonts/RobotoMono-Regular.ttf"
"${PROJECT_SOURCE_DIR}/assets/fonts/OFL.txt"
"${PROJECT_SOURCE_DIR}/assets/fonts/README.md"
"$<TARGET_FILE_DIR:${target}>/assets/fonts"
VERBATIM)
add_dependencies(${target} ${target}_font_bundle)
add_custom_command(TARGET ${target} POST_BUILD
COMMAND "${qt_deploy_tool}" --release --no-translations --no-compiler-runtime
"$<TARGET_FILE:${target}>"
VERBATIM)
endfunction()

qt_add_executable(termforge_qt_shell WIN32 src/qt/main.cpp)
set_target_properties(termforge_qt_shell PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qt-bin/$<CONFIG>")
termforge_qt_entry(termforge_qt_shell)

if(BUILD_TESTING)
find_package(Qt6 6.10.3 EXACT REQUIRED COMPONENTS Test)
add_executable(qt_cpp_shell_smoke src/qt/shell_smoke.cpp)
set_target_properties(qt_cpp_shell_smoke PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qt-smoke-bin/$<CONFIG>")
termforge_qt_entry(qt_cpp_shell_smoke)
target_link_libraries(qt_cpp_shell_smoke PRIVATE Qt6::Test)
# windeployqt deploys the Windows product plugin; this independent
# non-input test uses Qt's offscreen platform and needs its own plugin.
add_custom_command(TARGET qt_cpp_shell_smoke POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"$<TARGET_FILE:Qt6::QOffscreenIntegrationPlugin>"
"$<TARGET_FILE_DIR:qt_cpp_shell_smoke>/platforms/"
VERBATIM)
add_test(NAME qt_cpp_shell COMMAND "${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/src/qt/run_smoke.py" "$<TARGET_FILE:qt_cpp_shell_smoke>")
set_tests_properties(qt_cpp_shell PROPERTIES TIMEOUT 35
ENVIRONMENT "QT_QPA_PLATFORM=offscreen")
endif()
endif()
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

An independent, fully open-source terminal project targeting WindTerm 2.7.0 feature, behavior, UI and performance parity. Windows 11 x64 first; Windows, Linux and macOS ultimately. Not affiliated with WindTerm or KingToolbox.

当前是有限 Windows 本地终端原型,**R1 基础文字交互已按限定合同验收,正在推进 R2 可靠本地终端与开发版**。主线已有 Alacritty 候选实验、首批流式字素、有限多会话和配置恢复;候选核心与实验 ConPTY 后端的联合配置、完整 TUI/输入、真实不同 DPI、性能预算和开发包仍需闭合。现状见[能力矩阵](docs/FEATURES.md),接续顺序见 [R2 交付路线与验收](docs/milestones/R2-local-terminal.md)。SSH/SFTP 属于 R3,完整 UI 与 WindTerm 性能等价均未验收。`TermForge` 是暂名。
当前是有限 Windows 本地终端原型,**R1 基础文字交互已按限定合同验收,正在推进 R2 可靠本地终端与开发版**。已有 Win32 原型、有限多会话与配置恢复,以及独立 Alacritty 参考实验。正式路线已确定为自主 C++ 核心、当前采用 Qt Widgets GUI 与随包固定微软 ConPTY;新配置的最小整合、核心能力缺口、完整 TUI/输入、真实不同 DPI、性能预算和开发包仍需闭合。现状见[能力矩阵](docs/FEATURES.md),接续顺序见 [R2 交付路线与验收](docs/milestones/R2-local-terminal.md)。SSH/SFTP 属于 R3,完整 UI 与 WindTerm 性能等价均未验收。`TermForge` 是暂名。

方向已经用户确认,依据见 [ADR-0006](docs/adr/0006-confirmed-project-direction.md):原版行为优先,内部结构不强求一致,但认真研究、借鉴原版;Alacritty 保留为候选,正式核心与 GUI/呈现选型待用户确认;Windows 本地 Shell 后端已确认采用[随包固定微软 ConPTY](docs/adr/0007-bundled-microsoft-conpty.md),整合与验收尚未完成。关键性能指标分别验收,具体允许波动范围后续与用户明确。
方向已经用户确认,依据见 [ADR-0006](docs/adr/0006-confirmed-project-direction.md):原版行为优先,内部结构不强求一致,但认真研究、借鉴原版;用户已确认[自主 C++ 核心与当前 Qt Widgets GUI 路线](docs/adr/0008-cpp-core-qt-widgets.md),Windows 本地 Shell 后端采用[随包固定微软 ConPTY](docs/adr/0007-bundled-microsoft-conpty.md)。Alacritty 保留为参考,路线确认与实现验收分开。关键性能指标分别验收,具体允许波动范围后续与用户明确。

## 开始使用

Expand All @@ -22,6 +22,8 @@ An independent, fully open-source terminal project targeting WindTerm 2.7.0 feat

## 项目地图

新的[自主 C++ / Qt 会话入口](src/qt/README.md)以显式构建开关提供固定 80×24 的最小本地会话;当前默认入口和上面的操作说明仍属于既有 Win32 原型。Qt 工作区、完整文字交互及动态 resize 尚待迁移,不能据此声明完整开发版。

| 要了解什么 | 唯一入口 |
|---|---|
| 已经做了什么、还缺什么 | [当前能力与目标矩阵](docs/FEATURES.md) |
Expand Down
2 changes: 2 additions & 0 deletions assets/fonts/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 工作区 UI 字体来源

新的 [Qt 迁移入口](../../src/qt/README.md)也私有加载该独立 OFL 资源,作为首批固定 ASCII 终端绘制的可复建字体;不把它视为完整 CJK/emoji fallback、Nerd Font 或原版终端字体等价。旧 Win32 的字号/度量结果与 Qt 分开验证。

`RobotoMono-Regular.ttf` 从 [Google Fonts 的 RobotoMono 官方仓库](https://github.com/googlefonts/RobotoMono)独立取得,未使用 WindTerm 发行物中的字体。固定上游提交为 [`895ec691990d041dd727c7b5afa3ce56525d98e6`](https://github.com/googlefonts/RobotoMono/tree/895ec691990d041dd727c7b5afa3ce56525d98e6),取得日期为 2026-10-23。这里只随附未修改的静态 Regular 字体及同一提交的许可文件;完整可编辑字体源、构建脚本在固定上游树中。

| 文件 | 上游固定原始文件 | 字节数 | SHA-256 |
Expand Down
35 changes: 35 additions & 0 deletions cmake/ConptyBackend.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Fixed Microsoft ConPTY deployment for the selected Windows product target.
include_guard(GLOBAL)
include("${CMAKE_CURRENT_LIST_DIR}/CopyConptyBackend.cmake")

function(termforge_use_bundled_conpty target)
if(NOT WIN32 OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message(FATAL_ERROR "The fixed ConPTY package requires Windows x64")
endif()
if(NOT TARGET ${target})
message(FATAL_ERROR "ConPTY target does not exist: ${target}")
endif()
if(NOT TERMFORGE_CONPTY_BACKEND_DIR OR NOT IS_ABSOLUTE "${TERMFORGE_CONPTY_BACKEND_DIR}")
message(FATAL_ERROR "TERMFORGE_CONPTY_BACKEND_DIR must name the absolute fixed-package directory")
endif()
get_filename_component(repository "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/.." ABSOLUTE)
set(manifest "${repository}/third_party/conpty/manifest.json")
set(license "${repository}/third_party/conpty/LICENSE")
termforge_validate_conpty_pair("${TERMFORGE_CONPTY_BACKEND_DIR}" "${manifest}" "${license}")
target_compile_definitions(${target} PRIVATE TERMFORGE_CONPTY_SIDEBYSIDE=1)
target_link_libraries(${target} PRIVATE bcrypt)

# An always-run dependency also repairs a missing bundle when no source
# requires relinking. The script checks the inputs again before copying.
add_custom_target(${target}_conpty_bundle
COMMAND "${CMAKE_COMMAND}"
"-DCONPTY_BUNDLE_INPUT_DIRECTORY=${TERMFORGE_CONPTY_BACKEND_DIR}"
"-DCONPTY_BUNDLE_MANIFEST=${manifest}"
"-DCONPTY_BUNDLE_LICENSE=${license}"
"-DCONPTY_BUNDLE_OUTPUT_DIRECTORY=$<TARGET_FILE_DIR:${target}>/conpty-backend"
-P "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/CopyConptyBackend.cmake"
DEPENDS "${TERMFORGE_CONPTY_BACKEND_DIR}/conpty.dll"
"${TERMFORGE_CONPTY_BACKEND_DIR}/OpenConsole.exe" "${manifest}" "${license}"
VERBATIM)
add_dependencies(${target} ${target}_conpty_bundle)
endfunction()
40 changes: 40 additions & 0 deletions cmake/CopyConptyBackend.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Shared configure/build check for the immutable pair in the package manifest.
function(termforge_validate_conpty_pair directory manifest license)
if(NOT IS_ABSOLUTE "${directory}" OR NOT IS_DIRECTORY "${directory}")
message(FATAL_ERROR "Fixed ConPTY input must be an absolute directory")
endif()
file(READ "${manifest}" metadata)
foreach(name conpty.dll OpenConsole.exe)
if(NOT EXISTS "${directory}/${name}")
message(FATAL_ERROR "Fixed ConPTY package is incomplete: ${name}")
endif()
string(JSON expected GET "${metadata}" files "${name}")
file(SHA256 "${directory}/${name}" actual)
if(NOT actual STREQUAL expected)
message(FATAL_ERROR "Fixed ConPTY identity mismatch: ${name}")
endif()
endforeach()
string(JSON expected_license GET "${metadata}" bundled_license_sha256)
file(SHA256 "${license}" actual_license)
if(NOT actual_license STREQUAL expected_license)
message(FATAL_ERROR "Fixed ConPTY license identity mismatch")
endif()
endfunction()

if(CMAKE_SCRIPT_MODE_FILE STREQUAL CMAKE_CURRENT_LIST_FILE)
termforge_validate_conpty_pair("${CONPTY_BUNDLE_INPUT_DIRECTORY}"
"${CONPTY_BUNDLE_MANIFEST}" "${CONPTY_BUNDLE_LICENSE}")
if(NOT IS_ABSOLUTE "${CONPTY_BUNDLE_OUTPUT_DIRECTORY}")
message(FATAL_ERROR "Fixed ConPTY output must be an absolute directory")
endif()
file(MAKE_DIRECTORY "${CONPTY_BUNDLE_OUTPUT_DIRECTORY}")
execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${CONPTY_BUNDLE_INPUT_DIRECTORY}/conpty.dll"
"${CONPTY_BUNDLE_INPUT_DIRECTORY}/OpenConsole.exe"
"${CONPTY_BUNDLE_MANIFEST}" "${CONPTY_BUNDLE_LICENSE}"
"${CONPTY_BUNDLE_OUTPUT_DIRECTORY}"
RESULT_VARIABLE copy_status)
if(NOT copy_status EQUAL 0)
message(FATAL_ERROR "Fixed ConPTY bundle copy failed: ${copy_status}")
endif()
endif()
Loading