The window is no longer one file pretending to be a chat box. The transcript, the run panel, the composer and the two consent dialogs are their own QML components over one Theme singleton, and the shell around them adapts to the screen: docked and scrollable on a desktop, a slide-out drawer on a phone. QML (app/qml/, new except Main.qml): - Theme.qml: single QML singleton, dark Breeze-flavoured tokens (surface, border, accent, text/textDim/textFaint, radius, pad, gap, sizes, monospace); diffLineColor() for unified-diff rows, compactBreakpoint 880, touchTarget 34, segmentHeight 30, headerHeight 42. - ChatView.qml: a row-type dispatcher over the transcript model (message, thought, tool, note), follow-the-tail scrolling with a "jump to latest" button once the user scrolls up, and an empty state that says where the agent runs and what it can do. - MessageCard.qml, ThinkBlock.qml, NoteLine.qml, ToolCard.qml (status pill, arguments, collapsible output, 500-line cut, diff colouring, spinner while the call runs), TodoPanel.qml. - Sidebar.qml: workspace/provider/status, mode and approval segmented rows, plan switch, the engine's todo list, version footer; scrolls, so a short window still reaches every control. - Composer.qml: auto-growing field, Enter sends / Shift+Enter newlines, red Stop while a turn runs, autocorrect hints off, finger-sized targets. - ConsentDialog.qml (Deny / Once / Always -> CST_PERM_*) and AskDialog.qml (the model's options become buttons, free text stays possible). - Main.qml: shell with header, draggable sidebar splitter (hover feedback, 200-560 px, double-click resets), transcript, composer, the two dialogs, Esc to cancel and Ctrl+L to clear. Under 880 px the sidebar becomes a drawer with a scrim instead of squeezing the chat away. AgentCore / main.cpp: - provider, model, approval and planMode are properties now; approval and plan mode write back into the engine's config, and renderMarkdown() calls cst_web_md_html(), so a table or a code fence looks like the WebUI's. - the todo hook writes m_todo on the GUI thread (it was written from the worker thread before: a data race on a QString member). - --size WxH for the window (the layout tests drive it), and the Basic style is chosen by default: the Breeze Quick style registers its types with relative URLs, which floods the log and breaks the composer. UI tests (app/src/ui/): - --uitest drives the real window with synthesized input and exits with the number of failures: 15 checks over docking/drawer transitions, mouse clicks and touch taps reaching the engine's mode, the plan switch, the composer arming the send button, and a 460x900 -> 1200x820 resize round trip. - every control the tests touch carries an objectName; the sidebar's implicitHeight binding loop is gone. Tests: cst-app-selftest and --uitest pass in the standalone app build, ctest cst-app-selftest passes in the repo build (38/38 on the host). Version 0.8.8.
127 lines
5.3 KiB
CMake
127 lines
5.3 KiB
CMake
cmake_minimum_required(VERSION 3.16)
|
|
project(cst-agent-app LANGUAGES C CXX VERSION 0.1.0)
|
|
|
|
# The Qt front end (app/) embeds the C engine in-process: it links libcstcore
|
|
# and never opens a port. Two targets:
|
|
#
|
|
# cst-app-selftest pure C smoke test of the embedding path (no Qt), so the
|
|
# C side is still guarded on machines without Qt Quick
|
|
# cst-agent-app the GUI (Qt 6 Quick + QML), built when the Qt modules
|
|
# are there
|
|
#
|
|
# Standalone build (builds the engine from ../ too):
|
|
# cmake -S app -B build-app -DCMAKE_BUILD_TYPE=Debug && cmake --build build-app -j
|
|
# Inside the repo build (the parent defines the cstcore target, shared with the CLI):
|
|
# cmake -B build -DCST_BUILD_APP=ON && cmake --build build --target cst-agent-app
|
|
#
|
|
# Extra QML import paths, e.g. a Kirigami installed away from the Qt prefix:
|
|
# -DCST_APP_QML_IMPORT=... (added to QML2_IMPORT_PATH / the app's runtime)
|
|
|
|
option(CST_APP_GUI "build the Qt GUI (needs Qt6 Quick, Qml, QuickControls2)" ON)
|
|
option(CST_APP_SELFTEST "build the C embedding smoke test" ON)
|
|
|
|
# --- the engine ----------------------------------------------------------
|
|
# Used as a subproject (../CMakeLists.txt already defined cstcore, engine
|
|
# compiled once for CLI and app) or built here from the sources next door.
|
|
if(NOT TARGET cstcore)
|
|
set(CST_APP_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
|
include_directories("${CST_APP_ROOT}/include" "${CST_APP_ROOT}/vendor/cjson")
|
|
file(GLOB CST_APP_CORE_SRC
|
|
"${CST_APP_ROOT}/src/*.c"
|
|
"${CST_APP_ROOT}/src/tools/*.c"
|
|
"${CST_APP_ROOT}/vendor/cjson/cJSON.c")
|
|
list(REMOVE_ITEM CST_APP_CORE_SRC
|
|
"${CST_APP_ROOT}/src/main.c"
|
|
"${CST_APP_ROOT}/src/tui.c"
|
|
"${CST_APP_ROOT}/src/tui_stub.c"
|
|
"${CST_APP_ROOT}/src/textsel.c")
|
|
# The HTTP front end is not part of an in-process app: no server, no port.
|
|
list(REMOVE_ITEM CST_APP_CORE_SRC
|
|
"${CST_APP_ROOT}/src/net.c"
|
|
"${CST_APP_ROOT}/src/http_srv.c"
|
|
"${CST_APP_ROOT}/src/webui.c"
|
|
"${CST_APP_ROOT}/src/web_md.c")
|
|
if(EXISTS "${CST_APP_ROOT}/.gen/models_builtin.inc")
|
|
include_directories("${CST_APP_ROOT}/.gen")
|
|
endif()
|
|
add_library(cstcore STATIC ${CST_APP_CORE_SRC})
|
|
target_compile_definitions(cstcore PRIVATE CST_NO_WEBUI=1)
|
|
find_package(CURL REQUIRED)
|
|
find_package(Threads REQUIRED)
|
|
target_link_libraries(cstcore PUBLIC ${CURL_LIBRARIES} Threads::Threads m)
|
|
message(STATUS "app: building the engine from ${CST_APP_ROOT}")
|
|
else()
|
|
message(STATUS "app: linking the cstcore target of the parent build")
|
|
endif()
|
|
|
|
# --- the C smoke test ----------------------------------------------------
|
|
if(CST_APP_SELFTEST)
|
|
add_executable(cst-app-selftest tools/selftest.c)
|
|
target_link_libraries(cst-app-selftest PRIVATE cstcore)
|
|
enable_testing()
|
|
add_test(NAME cst-app-selftest COMMAND cst-app-selftest)
|
|
endif()
|
|
|
|
# --- the GUI -------------------------------------------------------------
|
|
if(CST_APP_GUI)
|
|
find_package(Qt6 6.4 COMPONENTS Core Gui Qml Quick QuickControls2 Test QUIET)
|
|
if(NOT Qt6_FOUND)
|
|
message(WARNING
|
|
"app: Qt 6 Quick/Qml not found, skipping cst-agent-app "
|
|
"(the C selftest still builds; install e.g. qt6.qtdeclarative / "
|
|
"qt6.qtquickcontrols2 and reconfigure)")
|
|
else()
|
|
qt_standard_project_setup()
|
|
# Qt 6.5+ policies: keep the module resource at :/qt/qml/<URI>/ (what
|
|
# main.cpp loads) and let CMake generate the qmldir of qml/ for us.
|
|
qt_policy(SET QTP0001 NEW)
|
|
qt_policy(SET QTP0004 NEW)
|
|
qt_add_executable(cst-agent-app
|
|
src/main.cpp
|
|
src/core/AgentCore.cpp
|
|
src/core/ToolProvider.cpp
|
|
src/ui/UiTest.cpp
|
|
src/platform/linux/NotifyTool.cpp)
|
|
target_include_directories(cst-agent-app PRIVATE
|
|
src "${CMAKE_CURRENT_SOURCE_DIR}/../include")
|
|
target_compile_definitions(cst-agent-app PRIVATE CST_APP_VERSION="${PROJECT_VERSION}")
|
|
if(CST_APP_QML_IMPORT)
|
|
target_compile_definitions(cst-agent-app PRIVATE
|
|
CST_APP_QML_IMPORT="${CST_APP_QML_IMPORT}")
|
|
endif()
|
|
target_link_libraries(cst-agent-app PRIVATE
|
|
Qt6::Core Qt6::Gui Qt6::Qml Qt6::Quick Qt6::QuickControls2 Qt6::Test cstcore)
|
|
if(WIN32)
|
|
set_target_properties(cst-agent-app PROPERTIES WIN32_EXECUTABLE TRUE)
|
|
endif()
|
|
|
|
# Theme is read by every component, so it is a QML singleton (one instance,
|
|
# no object needed in the tree).
|
|
set_source_files_properties(qml/Theme.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
|
|
# Keep the module flat in the resource tree (:qt/qml/CstAgent/Main.qml).
|
|
# Sources live in qml/, but a module whose files sit in a subdirectory of
|
|
# the URI makes Qt register the types with relative URLs ("qmlRegisterType
|
|
# requires absolute URLs" on every start).
|
|
foreach(_qml Main ChatView Sidebar MessageCard ToolCard ThinkBlock NoteLine
|
|
TodoPanel Composer ConsentDialog AskDialog Theme)
|
|
set_source_files_properties(qml/${_qml}.qml PROPERTIES QT_RESOURCE_ALIAS ${_qml}.qml)
|
|
endforeach()
|
|
qt_add_qml_module(cst-agent-app
|
|
URI CstAgent
|
|
VERSION 1.0
|
|
QML_FILES
|
|
qml/Main.qml
|
|
qml/ChatView.qml
|
|
qml/Sidebar.qml
|
|
qml/MessageCard.qml
|
|
qml/ToolCard.qml
|
|
qml/ThinkBlock.qml
|
|
qml/NoteLine.qml
|
|
qml/TodoPanel.qml
|
|
qml/Composer.qml
|
|
qml/ConsentDialog.qml
|
|
qml/AskDialog.qml
|
|
qml/Theme.qml)
|
|
endif()
|
|
endif()
|