@dusted/anqst 1.7.2 → 1.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ set(ANQST_WEBBASE_ABI_HASH_STAMP "_da43ea0d526e8ac0a869ccc79976519d2566b40d8b97a12bddfa2aaaac5304f6")
@@ -0,0 +1,116 @@
1
+ cmake_minimum_required(VERSION 3.21)
2
+ project(AnQstWebBase VERSION 0.1.0 LANGUAGES CXX)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+ set(CMAKE_AUTOMOC ON)
7
+ set(CMAKE_AUTOUIC ON)
8
+ set(CMAKE_AUTORCC ON)
9
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
10
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin")
11
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin")
12
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin")
13
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_BINARY_DIR}/bin")
14
+
15
+ option(ANQSTWEBBASE_BUILD_TESTS "Build AnQstWebBase unit tests" ON)
16
+
17
+ include("${CMAKE_CURRENT_SOURCE_DIR}/AnQstWebBaseAbi.cmake" OPTIONAL)
18
+ if(NOT DEFINED ANQST_WEBBASE_ABI_HASH_STAMP OR ANQST_WEBBASE_ABI_HASH_STAMP STREQUAL "_local_0")
19
+ string(TIMESTAMP ANQST_WEBBASE_LOCAL_EPOCH "%s")
20
+ set(ANQST_WEBBASE_ABI_HASH_STAMP "_local_${ANQST_WEBBASE_LOCAL_EPOCH}")
21
+ endif()
22
+ set(ANQST_WEBBASE_TARGET "anqstwebhost${ANQST_WEBBASE_ABI_HASH_STAMP}")
23
+ configure_file(
24
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/AnQstWebBaseAbi.h.in"
25
+ "${CMAKE_CURRENT_BINARY_DIR}/generated/AnQstWebBaseAbi.h"
26
+ @ONLY
27
+ )
28
+
29
+ find_package(Qt5 REQUIRED COMPONENTS Core Widgets Network WebEngineWidgets WebChannel)
30
+
31
+ get_target_property(QT5_CORE_DLL_RELEASE Qt5::Core IMPORTED_LOCATION_RELEASE)
32
+ if(NOT QT5_CORE_DLL_RELEASE)
33
+ get_target_property(QT5_CORE_DLL_RELEASE Qt5::Core IMPORTED_LOCATION)
34
+ endif()
35
+ get_filename_component(QT5_BIN_DIR "${QT5_CORE_DLL_RELEASE}" DIRECTORY)
36
+ find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${QT5_BIN_DIR}")
37
+
38
+ function(anqst_configure_qt_runtime target_name)
39
+ set_target_properties(${target_name} PROPERTIES
40
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
41
+ RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG}"
42
+ RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE}"
43
+ RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO}"
44
+ RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL}"
45
+ )
46
+
47
+ if(WIN32 AND WINDEPLOYQT_EXECUTABLE)
48
+ add_custom_command(
49
+ TARGET ${target_name}
50
+ POST_BUILD
51
+ COMMAND "${WINDEPLOYQT_EXECUTABLE}"
52
+ --no-translations
53
+ --no-compiler-runtime
54
+ --no-opengl-sw
55
+ --release
56
+ --dir "$<TARGET_FILE_DIR:${target_name}>"
57
+ "$<TARGET_FILE:${target_name}>"
58
+ COMMENT "Deploying Qt runtime files for ${target_name}"
59
+ VERBATIM
60
+ )
61
+ endif()
62
+ endfunction()
63
+
64
+ if(NOT TARGET ${ANQST_WEBBASE_TARGET})
65
+ add_library(${ANQST_WEBBASE_TARGET} STATIC
66
+ src/AnQstBase93.cpp
67
+ src/AnQstBase93.h
68
+ src/AnQstBridgeProxy.cpp
69
+ src/AnQstBridgeProxy.h
70
+ src/AnQstWidgetDebugDialog.cpp
71
+ src/AnQstWidgetDebugDialog.h
72
+ src/AnQstWebHostBase.cpp
73
+ src/AnQstWebHostBase.h
74
+ src/AnQstHostBridgeFacade.cpp
75
+ src/AnQstHostBridgeFacade.h
76
+ src/AngularHttpBaseServer.cpp
77
+ src/AngularHttpBaseServer.h
78
+ src/UI/AnQstWidgetDebugDialog.ui
79
+ "${CMAKE_CURRENT_BINARY_DIR}/generated/AnQstWebBaseAbi.h"
80
+ )
81
+ set_target_properties(${ANQST_WEBBASE_TARGET} PROPERTIES
82
+ AUTOUIC_SEARCH_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/src/UI"
83
+ )
84
+
85
+ target_link_libraries(${ANQST_WEBBASE_TARGET}
86
+ PUBLIC
87
+ Qt5::Core
88
+ Qt5::Widgets
89
+ Qt5::Network
90
+ Qt5::WebEngineWidgets
91
+ Qt5::WebChannel
92
+ )
93
+
94
+ target_include_directories(${ANQST_WEBBASE_TARGET}
95
+ PUBLIC
96
+ "${CMAKE_CURRENT_BINARY_DIR}/generated"
97
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
98
+ )
99
+ endif()
100
+
101
+ if(NOT TARGET anqstwebhost)
102
+ add_library(anqstwebhost ALIAS ${ANQST_WEBBASE_TARGET})
103
+ else()
104
+ get_target_property(ANQST_WEBBASE_EXISTING_ALIAS anqstwebhost ALIASED_TARGET)
105
+ if(NOT ANQST_WEBBASE_EXISTING_ALIAS)
106
+ set(ANQST_WEBBASE_EXISTING_ALIAS "non-alias target anqstwebhost")
107
+ endif()
108
+ if(NOT ANQST_WEBBASE_EXISTING_ALIAS STREQUAL ANQST_WEBBASE_TARGET)
109
+ message(STATUS "${CMAKE_CURRENT_SOURCE_DIR} exports ${ANQST_WEBBASE_TARGET} while shorthand anqstwebhost exports ${ANQST_WEBBASE_EXISTING_ALIAS}")
110
+ endif()
111
+ endif()
112
+
113
+ if(ANQSTWEBBASE_BUILD_TESTS)
114
+ enable_testing()
115
+ add_subdirectory(tests)
116
+ endif()
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": 4,
3
+ "configurePresets": [
4
+ {
5
+ "name": "linux-debug",
6
+ "displayName": "Linux Debug (system packages)",
7
+ "generator": "Ninja",
8
+ "binaryDir": "${sourceDir}/build",
9
+ "cacheVariables": {
10
+ "CMAKE_BUILD_TYPE": "Debug"
11
+ }
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,65 @@
1
+ # AnQstWebBase Documentation
2
+
3
+ This directory is a future-facing documentation area for designing a reusable Qt host base:
4
+ `AnQstWebHostBase` (inherits from `QWidget` and owns an internal `QWebEngineView`), intended to support AnQst-generated widget classes.
5
+
6
+ ## Scope and constraints
7
+
8
+ - Host any web app from **QRC** or **local filesystem directory**.
9
+ - Default runtime remains embedded/local (`QWebEngine` + `QWebChannel`).
10
+ - Development mode can be enabled with `enableDebug()` to:
11
+ - blank in-situ widget rendering,
12
+ - serve app assets over HTTP from configured content root,
13
+ - bridge AnQst transport over WebSocket for browser-attached development tools.
14
+ - HTTP binding defaults to localhost and can be widened for LAN testing.
15
+ - Host-owned bridge bootstrap: web apps must not include Qt `qwebchannel.js` script tags manually.
16
+ - Embedded host diagnostics are exposed through `onWebEngineError(const QString& channel, const QString& detail)`.
17
+ - The native `QWebEngine` subset is the baseline and remains available even if JavaScript never initializes, bridge bootstrap never becomes usable, or the renderer crashes.
18
+ - JavaScript stack traces are additive enrichment captured through an early `DocumentCreation` script and surfaced through the same signal when the renderer and JS engine stay alive long enough to produce them.
19
+ - External-browser host mode is out of scope for this signal.
20
+
21
+ ## Documentation structure
22
+
23
+ - `PHASE-1-Host-Base.md`
24
+ High-level API and behavior shape for `AnQstWebHostBase`.
25
+ - `PHASE-2-AnQstGen-Mapping.md`
26
+ How AnQst spec concepts map onto the host base contract.
27
+ - `PHASE-3-Widget-Specific-Generation.md`
28
+ How generator output can derive spec-specific widgets from the base.
29
+ - `PHASE-4-Validation-Adoption.md`
30
+ Validation strategy and rollout phases.
31
+
32
+ ## Design goal
33
+
34
+ Keep host APIs narrow and predictable so `AnQstGen` can generate a spec-specific widget class with minimal custom glue code.
35
+
36
+ ## Shared codec runtime helpers
37
+
38
+ - `src/AnQstBase93.h` / `src/AnQstBase93.cpp` provide the shared C++ base93 encoder/decoder used by generated widget codecs.
39
+ - Generated widgets call these helpers directly instead of emitting per-widget base93 implementations.
40
+
41
+ ## Current implementation note
42
+
43
+ - The demo application in `../../Examples/demo/` is wired through generated C++ widget output (`../../Examples/demo/lib/demo-widget/generated_output/DemoHostWidget_QtWidget`) and links only against the autogenerated widget library target (`DemoHostWidgetWidget`).
44
+ - The generated widget library carries the embedded Angular assets (`<WidgetName>.qrc` + `webapp/*`), so the Qt host app does not own web asset/QRC wiring.
45
+ - The demo consumes `../../Examples/demo/lib/demo-widget/anqst-cmake/CMakeLists.txt` as the widget dependency entrypoint.
46
+ - The previous standalone demo placeholder page (`../../Examples/demo/hello.html`) is removed from the canonical demo path.
47
+
48
+ ## Build notes
49
+
50
+ - Linux (Ubuntu): use system packages only
51
+ - Windows: Wild wild west, amirite?
52
+
53
+ ### Ubuntu quick start
54
+
55
+ From workspace root:
56
+
57
+ ```bash
58
+ sudo ./install_dependencies.sh
59
+ ```
60
+
61
+ Then build:
62
+
63
+ ```bash
64
+ Look at Examples/build.sh
65
+ ```
@@ -0,0 +1,91 @@
1
+ #include "AnQstBase93.h"
2
+
3
+ #include <QChar>
4
+
5
+ #include <cstddef>
6
+
7
+ namespace ANQST_WEBBASE_NAMESPACE {
8
+
9
+ namespace {
10
+ constexpr char kBase93Alphabet[] =
11
+ " !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~";
12
+
13
+ inline int base93AlphabetIndex(QChar c) {
14
+ const unsigned int uc = c.unicode();
15
+ return static_cast<int>(uc) - 32 - static_cast<int>(uc > 34u) - static_cast<int>(uc > 92u);
16
+ }
17
+ } // namespace
18
+
19
+ QString anqstBase93Encode(const std::vector<std::uint8_t>& bytes) {
20
+ const std::size_t inputSize = bytes.size();
21
+ const std::size_t fullBlocks = inputSize >> 2;
22
+ const std::size_t remainder = inputSize & 3u;
23
+ QString output(static_cast<qsizetype>(fullBlocks * 5 + (remainder ? remainder + 1 : 0)), QChar(u'\0'));
24
+ QChar* out = output.data();
25
+ std::size_t writeOffset = 0;
26
+ for (std::size_t i = 0; i < fullBlocks; ++i) {
27
+ const std::size_t byteOffset = i << 2;
28
+ std::uint32_t value =
29
+ (static_cast<std::uint32_t>(bytes[byteOffset]) << 24) |
30
+ (static_cast<std::uint32_t>(bytes[byteOffset + 1]) << 16) |
31
+ (static_cast<std::uint32_t>(bytes[byteOffset + 2]) << 8) |
32
+ static_cast<std::uint32_t>(bytes[byteOffset + 3]);
33
+ out[static_cast<qsizetype>(writeOffset + 4)] = QChar::fromLatin1(kBase93Alphabet[value % 93u]);
34
+ value /= 93u;
35
+ out[static_cast<qsizetype>(writeOffset + 3)] = QChar::fromLatin1(kBase93Alphabet[value % 93u]);
36
+ value /= 93u;
37
+ out[static_cast<qsizetype>(writeOffset + 2)] = QChar::fromLatin1(kBase93Alphabet[value % 93u]);
38
+ value /= 93u;
39
+ out[static_cast<qsizetype>(writeOffset + 1)] = QChar::fromLatin1(kBase93Alphabet[value % 93u]);
40
+ out[static_cast<qsizetype>(writeOffset)] = QChar::fromLatin1(kBase93Alphabet[value / 93u]);
41
+ writeOffset += 5;
42
+ }
43
+ if (remainder != 0u) {
44
+ const std::size_t byteOffset = fullBlocks << 2;
45
+ std::uint32_t value = 0;
46
+ for (std::size_t j = 0; j < remainder; ++j) {
47
+ value = (value << 8) | bytes[byteOffset + j];
48
+ }
49
+ for (std::size_t j = remainder + 1; j-- > 0;) {
50
+ out[static_cast<qsizetype>(writeOffset + j)] = QChar::fromLatin1(kBase93Alphabet[value % 93u]);
51
+ value /= 93u;
52
+ }
53
+ }
54
+ return output;
55
+ }
56
+
57
+ std::vector<std::uint8_t> anqstBase93Decode(const QString& encoded) {
58
+ const std::size_t inputSize = static_cast<std::size_t>(encoded.size());
59
+ const std::size_t fullBlocks = inputSize / 5;
60
+ const std::size_t remainder = inputSize - fullBlocks * 5;
61
+ std::vector<std::uint8_t> output(fullBlocks * 4 + (remainder ? remainder - 1 : 0));
62
+ const QChar* in = encoded.constData();
63
+ std::size_t writeOffset = 0;
64
+ for (std::size_t i = 0; i < fullBlocks; ++i) {
65
+ const std::size_t charOffset = i * 5;
66
+ std::uint32_t value = static_cast<std::uint32_t>(base93AlphabetIndex(in[static_cast<qsizetype>(charOffset)]));
67
+ value = value * 93u + static_cast<std::uint32_t>(base93AlphabetIndex(in[static_cast<qsizetype>(charOffset + 1)]));
68
+ value = value * 93u + static_cast<std::uint32_t>(base93AlphabetIndex(in[static_cast<qsizetype>(charOffset + 2)]));
69
+ value = value * 93u + static_cast<std::uint32_t>(base93AlphabetIndex(in[static_cast<qsizetype>(charOffset + 3)]));
70
+ value = value * 93u + static_cast<std::uint32_t>(base93AlphabetIndex(in[static_cast<qsizetype>(charOffset + 4)]));
71
+ output[writeOffset] = static_cast<std::uint8_t>(value >> 24);
72
+ output[writeOffset + 1] = static_cast<std::uint8_t>((value >> 16) & 255u);
73
+ output[writeOffset + 2] = static_cast<std::uint8_t>((value >> 8) & 255u);
74
+ output[writeOffset + 3] = static_cast<std::uint8_t>(value & 255u);
75
+ writeOffset += 4;
76
+ }
77
+ if (remainder != 0u) {
78
+ std::uint32_t value = 0;
79
+ for (std::size_t i = 0; i < remainder; ++i) {
80
+ value = value * 93u + static_cast<std::uint32_t>(
81
+ base93AlphabetIndex(in[static_cast<qsizetype>(fullBlocks * 5 + i)]));
82
+ }
83
+ for (std::size_t i = remainder - 1; i-- > 0;) {
84
+ output[writeOffset + i] = static_cast<std::uint8_t>(value & 255u);
85
+ value /= 256u;
86
+ }
87
+ }
88
+ return output;
89
+ }
90
+
91
+ } // namespace ANQST_WEBBASE_NAMESPACE
@@ -0,0 +1,15 @@
1
+ #pragma once
2
+
3
+ #include "AnQstWebBaseAbi.h"
4
+
5
+ #include <QString>
6
+
7
+ #include <cstdint>
8
+ #include <vector>
9
+
10
+ namespace ANQST_WEBBASE_NAMESPACE {
11
+
12
+ QString anqstBase93Encode(const std::vector<std::uint8_t>& bytes);
13
+ std::vector<std::uint8_t> anqstBase93Decode(const QString& encoded);
14
+
15
+ } // namespace ANQST_WEBBASE_NAMESPACE
@@ -0,0 +1,30 @@
1
+ #include "AnQstBridgeProxy.h"
2
+ #include "AnQstHostBridgeFacade.h"
3
+
4
+ namespace ANQST_WEBBASE_NAMESPACE {
5
+
6
+ AnQstBridgeProxy::AnQstBridgeProxy(AnQstHostBridgeFacade* facade, QObject* parent)
7
+ : QObject(parent)
8
+ , m_facade(facade) {}
9
+
10
+ void AnQstBridgeProxy::anQstBridge_registerSlot(const QString& service, const QString& member) {
11
+ m_facade->registerSlot(service, member);
12
+ }
13
+
14
+ QVariant AnQstBridgeProxy::anQstBridge_call(const QString& service, const QString& member, const QVariantList& args) {
15
+ return m_facade->call(service, member, args);
16
+ }
17
+
18
+ void AnQstBridgeProxy::anQstBridge_emit(const QString& service, const QString& member, const QVariantList& args) {
19
+ m_facade->emitMessage(service, member, args);
20
+ }
21
+
22
+ void AnQstBridgeProxy::anQstBridge_setInput(const QString& service, const QString& member, const QVariant& value) {
23
+ m_facade->setInput(service, member, value);
24
+ }
25
+
26
+ void AnQstBridgeProxy::anQstBridge_resolveSlot(const QString& requestId, bool ok, const QVariant& payload, const QString& error) {
27
+ m_facade->resolveSlot(requestId, ok, payload, error);
28
+ }
29
+
30
+ } // namespace ANQST_WEBBASE_NAMESPACE
@@ -0,0 +1,41 @@
1
+ #pragma once
2
+
3
+ #include "AnQstWebBaseAbi.h"
4
+
5
+ #include <QObject>
6
+ #include <QString>
7
+ #include <QVariant>
8
+ #include <QVariantList>
9
+
10
+ namespace ANQST_WEBBASE_NAMESPACE {
11
+
12
+ class AnQstHostBridgeFacade;
13
+
14
+ // Thin QObject registered with QWebChannel as the sole bridge endpoint.
15
+ // Inherits only QObject so QWebChannel sees no QWidget/QObject properties
16
+ // lacking NOTIFY signals, producing zero "no notify signal" warnings.
17
+ class AnQstBridgeProxy : public QObject {
18
+ Q_OBJECT
19
+
20
+ public:
21
+ explicit AnQstBridgeProxy(AnQstHostBridgeFacade* facade, QObject* parent = nullptr);
22
+
23
+ Q_INVOKABLE void anQstBridge_registerSlot(const QString& service, const QString& member);
24
+ Q_INVOKABLE QVariant anQstBridge_call(const QString& service, const QString& member, const QVariantList& args);
25
+ Q_INVOKABLE void anQstBridge_emit(const QString& service, const QString& member, const QVariantList& args);
26
+ Q_INVOKABLE void anQstBridge_setInput(const QString& service, const QString& member, const QVariant& value);
27
+ Q_INVOKABLE void anQstBridge_resolveSlot(const QString& requestId, bool ok, const QVariant& payload, const QString& error);
28
+
29
+ signals:
30
+ void anQstBridge_outputUpdated(const QString& service, const QString& member, const QVariant& value);
31
+ void anQstBridge_slotInvocationRequested(const QString& requestId, const QString& service, const QString& member, const QVariantList& args);
32
+ void anQstBridge_hostDiagnostic(const QVariantMap& payload);
33
+ void anQstBridge_dropReceived(const QString& service, const QString& member, const QVariant& payload, double x, double y);
34
+ void anQstBridge_hoverUpdated(const QString& service, const QString& member, const QVariant& payload, double x, double y);
35
+ void anQstBridge_hoverLeft(const QString& service, const QString& member);
36
+
37
+ private:
38
+ AnQstHostBridgeFacade* m_facade;
39
+ };
40
+
41
+ } // namespace ANQST_WEBBASE_NAMESPACE