@dusted/anqst 0.1.3 → 1.0.1
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.
- package/dist/src/app.js +102 -77
- package/dist/src/build-stamp.js +1 -1
- package/dist/src/emit.js +231 -6
- package/dist/src/parser.js +53 -5
- package/package.json +1 -1
- package/spec/AnQst-Spec-DSL.d.ts +328 -249
package/dist/src/app.js
CHANGED
|
@@ -19,7 +19,7 @@ const project_1 = require("./project");
|
|
|
19
19
|
const layout_1 = require("./layout");
|
|
20
20
|
const parser_1 = require("./parser");
|
|
21
21
|
const verify_1 = require("./verify");
|
|
22
|
-
const
|
|
22
|
+
const ANQSTGEN_ACTIVE_STAMP_FILE = ".anqstgen-version-active.json";
|
|
23
23
|
function renderHelp() {
|
|
24
24
|
const version = readActiveBuildStamp();
|
|
25
25
|
return [
|
|
@@ -135,8 +135,27 @@ function runTest(cwd) {
|
|
|
135
135
|
message: verification.message
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
|
+
function resolveAnQstGenRoot() {
|
|
139
|
+
return node_path_1.default.resolve(__dirname, "..", "..");
|
|
140
|
+
}
|
|
138
141
|
function readActiveBuildStamp() {
|
|
139
|
-
|
|
142
|
+
if (process.env.ANQST_BUILD_STAMP && process.env.ANQST_BUILD_STAMP.trim().length > 0) {
|
|
143
|
+
return process.env.ANQST_BUILD_STAMP.trim();
|
|
144
|
+
}
|
|
145
|
+
const activePath = node_path_1.default.join(resolveAnQstGenRoot(), ANQSTGEN_ACTIVE_STAMP_FILE);
|
|
146
|
+
if (!node_fs_1.default.existsSync(activePath)) {
|
|
147
|
+
return "unknown_build_0";
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(activePath, "utf8"));
|
|
151
|
+
if (typeof parsed.active === "string" && parsed.active.trim().length > 0) {
|
|
152
|
+
return parsed.active.trim();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// Fallback to deterministic unknown stamp.
|
|
157
|
+
}
|
|
158
|
+
return "unknown_build_0";
|
|
140
159
|
}
|
|
141
160
|
function runDesignerPluginBuild(cwd, widgetName) {
|
|
142
161
|
const layout = (0, layout_1.resolveGeneratedLayoutPaths)(cwd, widgetName);
|
|
@@ -176,94 +195,100 @@ function runDesignerPluginBuild(cwd, widgetName) {
|
|
|
176
195
|
}
|
|
177
196
|
function runBuild(cwd, designerPlugin = false) {
|
|
178
197
|
const buildVersion = readActiveBuildStamp();
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const outputs = (0, emit_1.generateOutputs)(parsed, generationTargets);
|
|
189
|
-
(0, emit_1.writeGeneratedOutputs)(cwd, outputs);
|
|
190
|
-
if (generationTargets.emitQWidget) {
|
|
191
|
-
(0, emit_1.installQtIntegrationCMake)(cwd, parsed.widgetName);
|
|
192
|
-
}
|
|
193
|
-
const hasAngularProject = generationTargets.emitQWidget && node_fs_1.default.existsSync(node_path_1.default.join(cwd, "angular.json"));
|
|
194
|
-
if (hasAngularProject) {
|
|
195
|
-
const angularBuild = (0, node_child_process_1.spawnSync)("npx", ["ng", "build", "--configuration", "production"], {
|
|
196
|
-
cwd,
|
|
197
|
-
stdio: "inherit",
|
|
198
|
-
shell: process.platform === "win32"
|
|
199
|
-
});
|
|
200
|
-
if (angularBuild.status !== 0) {
|
|
201
|
-
throw new errors_1.VerifyError("Angular build failed while preparing embedded widget assets.");
|
|
198
|
+
process.env.ANQST_BUILD_STAMP = buildVersion;
|
|
199
|
+
try {
|
|
200
|
+
const specPath = (0, project_1.resolveAnQstSpecPath)(cwd);
|
|
201
|
+
const configuredWidgetName = (0, project_1.resolveAnQstWidgetName)(cwd);
|
|
202
|
+
const generationTargets = resolveGenerationTargetsFromCwd(cwd, true);
|
|
203
|
+
const parsed = (0, parser_1.parseSpecFile)(specPath);
|
|
204
|
+
(0, verify_1.verifySpec)(parsed);
|
|
205
|
+
if (parsed.widgetName !== configuredWidgetName) {
|
|
206
|
+
throw new errors_1.VerifyError(`Settings widgetName '${configuredWidgetName}' does not match spec namespace '${parsed.widgetName}'.`);
|
|
202
207
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (
|
|
207
|
-
|
|
208
|
+
resetGeneratedTargets(cwd, parsed.widgetName, generationTargets);
|
|
209
|
+
const outputs = (0, emit_1.generateOutputs)(parsed, generationTargets);
|
|
210
|
+
(0, emit_1.writeGeneratedOutputs)(cwd, outputs);
|
|
211
|
+
if (generationTargets.emitQWidget) {
|
|
212
|
+
(0, emit_1.installQtIntegrationCMake)(cwd, parsed.widgetName);
|
|
208
213
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
+
const hasAngularProject = generationTargets.emitQWidget && node_fs_1.default.existsSync(node_path_1.default.join(cwd, "angular.json"));
|
|
215
|
+
if (hasAngularProject) {
|
|
216
|
+
const angularBuild = (0, node_child_process_1.spawnSync)("npx", ["ng", "build", "--configuration", "production"], {
|
|
217
|
+
cwd,
|
|
218
|
+
stdio: "inherit",
|
|
219
|
+
shell: process.platform === "win32"
|
|
220
|
+
});
|
|
221
|
+
if (angularBuild.status !== 0) {
|
|
222
|
+
throw new errors_1.VerifyError("Angular build failed while preparing embedded widget assets.");
|
|
223
|
+
}
|
|
214
224
|
}
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
(
|
|
218
|
-
|
|
219
|
-
|
|
225
|
+
if (generationTargets.emitQWidget) {
|
|
226
|
+
const embedded = (0, emit_1.installEmbeddedWebBundle)(cwd, parsed.widgetName);
|
|
227
|
+
if (hasAngularProject && !embedded) {
|
|
228
|
+
throw new errors_1.VerifyError("Unable to embed Angular output. Ensure ng build produced a dist bundle with index.html.");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
let designerPluginBuilt = false;
|
|
232
|
+
if (designerPlugin) {
|
|
233
|
+
if (!generationTargets.emitQWidget) {
|
|
234
|
+
console.warn("[AnQst] --designerplugin requested but QWidget target is not enabled. Skipping designer plugin build.");
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
const widgetCategory = (0, project_1.resolveAnQstWidgetCategory)(cwd);
|
|
238
|
+
(0, emit_1.installQtDesignerPluginCMake)(cwd, parsed.widgetName, { widgetCategory });
|
|
239
|
+
runDesignerPluginBuild(cwd, parsed.widgetName);
|
|
240
|
+
designerPluginBuilt = true;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (!generationTargets.emitAngularService && !generationTargets.emitQWidget && !generationTargets.emitNodeExpressWs) {
|
|
244
|
+
return {
|
|
245
|
+
success: true,
|
|
246
|
+
message: [
|
|
247
|
+
"Build completed.",
|
|
248
|
+
` anqst version ${buildVersion}`,
|
|
249
|
+
" No outputs selected by AnQst.generate."
|
|
250
|
+
].join("\n")
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const layout = (0, layout_1.resolveGeneratedLayoutPaths)(cwd, parsed.widgetName);
|
|
254
|
+
const detailLines = [];
|
|
255
|
+
if (generationTargets.emitAngularService) {
|
|
256
|
+
detailLines.push(" Target AngularService:");
|
|
257
|
+
detailLines.push(` - Services output: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.frontendRoot, "services"))}`);
|
|
258
|
+
detailLines.push(` - Types output: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.frontendRoot, "types"))}`);
|
|
259
|
+
}
|
|
260
|
+
if (generationTargets.emitQWidget) {
|
|
261
|
+
detailLines.push(" Target QWidget:");
|
|
262
|
+
detailLines.push(` - Qt integration CMake: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.cppCmakeRoot, "CMakeLists.txt"))}`);
|
|
263
|
+
detailLines.push(` - Widget output root: ${(0, layout_1.toProjectRelative)(cwd, layout.cppQtWidgetRoot)}`);
|
|
264
|
+
detailLines.push(" - Embedded web assets refreshed from Angular build");
|
|
265
|
+
}
|
|
266
|
+
if (generationTargets.emitNodeExpressWs) {
|
|
267
|
+
detailLines.push(" Target node_express_ws:");
|
|
268
|
+
detailLines.push(` - Module output root: ${(0, layout_1.toProjectRelative)(cwd, layout.nodeExpressRoot)}`);
|
|
269
|
+
}
|
|
270
|
+
if (designerPluginBuilt) {
|
|
271
|
+
const pluginBinaryPath = (0, layout_1.normalizeSlashes)(node_path_1.default.join((0, layout_1.toProjectRelative)(cwd, layout.designerPluginBuildRoot), designerPluginBinaryName(parsed.widgetName)));
|
|
272
|
+
detailLines.push(" Target QtDesignerPlugin:");
|
|
273
|
+
detailLines.push(` - Build output: ${(0, layout_1.toProjectRelative)(cwd, layout.designerPluginBuildRoot)}`);
|
|
274
|
+
detailLines.push(` - Plugin binary: ${pluginBinaryPath}`);
|
|
275
|
+
detailLines.push(" - Install target dir: <QT_INSTALL_PLUGINS>/designer");
|
|
276
|
+
detailLines.push(" - Discover QT_INSTALL_PLUGINS: qmake -query QT_INSTALL_PLUGINS");
|
|
277
|
+
detailLines.push(` - Example install: cp ${pluginBinaryPath} \"$(qmake -query QT_INSTALL_PLUGINS)/designer/\"`);
|
|
278
|
+
detailLines.push(` - User-local install: mkdir -p \"$HOME/.local/lib/qt5/plugins/designer\" && cp ${pluginBinaryPath} \"$HOME/.local/lib/qt5/plugins/designer/\"`);
|
|
220
279
|
}
|
|
221
|
-
}
|
|
222
|
-
if (!generationTargets.emitAngularService && !generationTargets.emitQWidget && !generationTargets.emitNodeExpressWs) {
|
|
223
280
|
return {
|
|
224
281
|
success: true,
|
|
225
282
|
message: [
|
|
226
283
|
"Build completed.",
|
|
227
284
|
` anqst version ${buildVersion}`,
|
|
228
|
-
|
|
285
|
+
...detailLines
|
|
229
286
|
].join("\n")
|
|
230
287
|
};
|
|
231
288
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (generationTargets.emitAngularService) {
|
|
235
|
-
detailLines.push(" Target AngularService:");
|
|
236
|
-
detailLines.push(` - Services output: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.frontendRoot, "services"))}`);
|
|
237
|
-
detailLines.push(` - Types output: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.frontendRoot, "types"))}`);
|
|
238
|
-
}
|
|
239
|
-
if (generationTargets.emitQWidget) {
|
|
240
|
-
detailLines.push(" Target QWidget:");
|
|
241
|
-
detailLines.push(` - Qt integration CMake: ${(0, layout_1.toProjectRelative)(cwd, node_path_1.default.join(layout.cppCmakeRoot, "CMakeLists.txt"))}`);
|
|
242
|
-
detailLines.push(` - Widget output root: ${(0, layout_1.toProjectRelative)(cwd, layout.cppQtWidgetRoot)}`);
|
|
243
|
-
detailLines.push(" - Embedded web assets refreshed from Angular build");
|
|
244
|
-
}
|
|
245
|
-
if (generationTargets.emitNodeExpressWs) {
|
|
246
|
-
detailLines.push(" Target node_express_ws:");
|
|
247
|
-
detailLines.push(` - Module output root: ${(0, layout_1.toProjectRelative)(cwd, layout.nodeExpressRoot)}`);
|
|
248
|
-
}
|
|
249
|
-
if (designerPluginBuilt) {
|
|
250
|
-
const pluginBinaryPath = (0, layout_1.normalizeSlashes)(node_path_1.default.join((0, layout_1.toProjectRelative)(cwd, layout.designerPluginBuildRoot), designerPluginBinaryName(parsed.widgetName)));
|
|
251
|
-
detailLines.push(" Target QtDesignerPlugin:");
|
|
252
|
-
detailLines.push(` - Build output: ${(0, layout_1.toProjectRelative)(cwd, layout.designerPluginBuildRoot)}`);
|
|
253
|
-
detailLines.push(` - Plugin binary: ${pluginBinaryPath}`);
|
|
254
|
-
detailLines.push(" - Install target dir: <QT_INSTALL_PLUGINS>/designer");
|
|
255
|
-
detailLines.push(" - Discover QT_INSTALL_PLUGINS: qmake -query QT_INSTALL_PLUGINS");
|
|
256
|
-
detailLines.push(` - Example install: cp ${pluginBinaryPath} \"$(qmake -query QT_INSTALL_PLUGINS)/designer/\"`);
|
|
257
|
-
detailLines.push(` - User-local install: mkdir -p \"$HOME/.local/lib/qt5/plugins/designer\" && cp ${pluginBinaryPath} \"$HOME/.local/lib/qt5/plugins/designer/\"`);
|
|
289
|
+
finally {
|
|
290
|
+
delete process.env.ANQST_BUILD_STAMP;
|
|
258
291
|
}
|
|
259
|
-
return {
|
|
260
|
-
success: true,
|
|
261
|
-
message: [
|
|
262
|
-
"Build completed.",
|
|
263
|
-
` anqst version ${buildVersion}`,
|
|
264
|
-
...detailLines
|
|
265
|
-
].join("\n")
|
|
266
|
-
};
|
|
267
292
|
}
|
|
268
293
|
function parseSpecCommandArg(commandName, specArg, extraArgs) {
|
|
269
294
|
const allArgs = [specArg, ...extraArgs].filter((arg) => typeof arg === "string" && arg.length > 0);
|
package/dist/src/build-stamp.js
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ANQST_BUILD_STAMP = void 0;
|
|
4
4
|
// Generated by scripts/build-with-stamp.js. Do not edit by hand.
|
|
5
|
-
exports.ANQST_BUILD_STAMP = "
|
|
5
|
+
exports.ANQST_BUILD_STAMP = "unknown/unknown_build_0/1970-01-01";
|
package/dist/src/emit.js
CHANGED
|
@@ -12,7 +12,6 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
12
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
13
|
const typescript_1 = __importDefault(require("typescript"));
|
|
14
14
|
const pngjs_1 = require("pngjs");
|
|
15
|
-
const build_stamp_1 = require("./build-stamp");
|
|
16
15
|
const layout_1 = require("./layout");
|
|
17
16
|
function stripAnQstType(typeText) {
|
|
18
17
|
return typeText
|
|
@@ -627,6 +626,27 @@ function buildCppTypeContext(spec) {
|
|
|
627
626
|
}
|
|
628
627
|
return normalizer.buildContext();
|
|
629
628
|
}
|
|
629
|
+
function collectDragDropMimeConstants(spec) {
|
|
630
|
+
const seen = new Set();
|
|
631
|
+
const constants = [];
|
|
632
|
+
for (const service of spec.services) {
|
|
633
|
+
for (const member of service.members) {
|
|
634
|
+
if ((member.kind === "DropTarget" || member.kind === "HoverTarget") && member.payloadTypeText) {
|
|
635
|
+
const typeName = member.payloadTypeText.replace(/\s/g, "");
|
|
636
|
+
const key = `${service.name}-${typeName}`;
|
|
637
|
+
if (seen.has(key))
|
|
638
|
+
continue;
|
|
639
|
+
seen.add(key);
|
|
640
|
+
constants.push({
|
|
641
|
+
typeName,
|
|
642
|
+
serviceName: service.name,
|
|
643
|
+
mimeType: `application/anqst-dragdropevent_${service.name}-${typeName}`
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return constants;
|
|
649
|
+
}
|
|
630
650
|
function renderTypesHeader(spec, cppTypes) {
|
|
631
651
|
const decls = cppTypes.orderedDecls.map(renderCppDecl).join("\n\n");
|
|
632
652
|
const metatypes = cppTypes.structNames
|
|
@@ -635,6 +655,11 @@ function renderTypesHeader(spec, cppTypes) {
|
|
|
635
655
|
`Q_DECLARE_METATYPE(QList<${spec.widgetName}::${name}>)`
|
|
636
656
|
])
|
|
637
657
|
.join("\n");
|
|
658
|
+
const mimeConstants = collectDragDropMimeConstants(spec);
|
|
659
|
+
const mimeConstantLines = mimeConstants
|
|
660
|
+
.map((c) => `static constexpr const char* kDragDropMime_${c.typeName} = "${c.mimeType}";`)
|
|
661
|
+
.join("\n");
|
|
662
|
+
const mimeBlock = mimeConstantLines.length > 0 ? `\n${mimeConstantLines}\n` : "";
|
|
638
663
|
return `#pragma once
|
|
639
664
|
#include <QString>
|
|
640
665
|
#include <QStringList>
|
|
@@ -647,7 +672,7 @@ function renderTypesHeader(spec, cppTypes) {
|
|
|
647
672
|
namespace ${spec.widgetName} {
|
|
648
673
|
|
|
649
674
|
${decls}
|
|
650
|
-
|
|
675
|
+
${mimeBlock}
|
|
651
676
|
} // namespace ${spec.widgetName}
|
|
652
677
|
|
|
653
678
|
${metatypes}
|
|
@@ -710,6 +735,15 @@ function renderWidgetHeader(spec, cppTypes) {
|
|
|
710
735
|
publicSlots.push(`void ${member.name}Slot(const ${cppType}& value);`);
|
|
711
736
|
}
|
|
712
737
|
}
|
|
738
|
+
else if (member.kind === "DropTarget" && member.payloadTypeText) {
|
|
739
|
+
const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
|
|
740
|
+
signals.push(`void ${member.name}(const ${cppType}& payload, double x, double y);`);
|
|
741
|
+
}
|
|
742
|
+
else if (member.kind === "HoverTarget" && member.payloadTypeText) {
|
|
743
|
+
const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
|
|
744
|
+
signals.push(`void ${member.name}(const ${cppType}& payload, double x, double y);`);
|
|
745
|
+
signals.push(`void ${member.name}Left();`);
|
|
746
|
+
}
|
|
713
747
|
}
|
|
714
748
|
}
|
|
715
749
|
return `#pragma once
|
|
@@ -860,6 +894,45 @@ function renderCppStub(spec, cppTypes) {
|
|
|
860
894
|
lines.push(` Q_UNUSED(kResourcesInitialized);`);
|
|
861
895
|
lines.push(` registerGeneratedMetaTypes();`);
|
|
862
896
|
lines.push(` installBridgeBindings();`);
|
|
897
|
+
for (const service of spec.services) {
|
|
898
|
+
for (const member of service.members) {
|
|
899
|
+
if (member.kind === "DropTarget" && member.payloadTypeText) {
|
|
900
|
+
const typeName = member.payloadTypeText.replace(/\s/g, "");
|
|
901
|
+
const mimeConst = `${spec.widgetName}::kDragDropMime_${typeName}`;
|
|
902
|
+
lines.push(` registerDropTarget(QStringLiteral("${service.name}"), QStringLiteral("${member.name}"), QString::fromUtf8(${mimeConst}));`);
|
|
903
|
+
}
|
|
904
|
+
else if (member.kind === "HoverTarget" && member.payloadTypeText) {
|
|
905
|
+
const typeName = member.payloadTypeText.replace(/\s/g, "");
|
|
906
|
+
const mimeConst = `${spec.widgetName}::kDragDropMime_${typeName}`;
|
|
907
|
+
lines.push(` registerHoverTarget(QStringLiteral("${service.name}"), QStringLiteral("${member.name}"), QString::fromUtf8(${mimeConst}), ${member.hoverThrottleMs});`);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
for (const service of spec.services) {
|
|
912
|
+
for (const member of service.members) {
|
|
913
|
+
if (member.kind === "DropTarget" && member.payloadTypeText) {
|
|
914
|
+
const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
|
|
915
|
+
lines.push(` QObject::connect(this, &AnQstWebHostBase::anQstBridge_dropReceived, this, [this](const QString& service, const QString& member, const QVariant& payload, double x, double y) {`);
|
|
916
|
+
lines.push(` if (service == QStringLiteral("${service.name}") && member == QStringLiteral("${member.name}")) {`);
|
|
917
|
+
lines.push(` emit ${member.name}(payload.value<${cppType}>(), x, y);`);
|
|
918
|
+
lines.push(` }`);
|
|
919
|
+
lines.push(` });`);
|
|
920
|
+
}
|
|
921
|
+
else if (member.kind === "HoverTarget" && member.payloadTypeText) {
|
|
922
|
+
const cppType = cppTypes.mapTypeText(member.payloadTypeText, [service.name, member.name, "Payload"]);
|
|
923
|
+
lines.push(` QObject::connect(this, &AnQstWebHostBase::anQstBridge_hoverUpdated, this, [this](const QString& service, const QString& member, const QVariant& payload, double x, double y) {`);
|
|
924
|
+
lines.push(` if (service == QStringLiteral("${service.name}") && member == QStringLiteral("${member.name}")) {`);
|
|
925
|
+
lines.push(` emit ${member.name}(payload.value<${cppType}>(), x, y);`);
|
|
926
|
+
lines.push(` }`);
|
|
927
|
+
lines.push(` });`);
|
|
928
|
+
lines.push(` QObject::connect(this, &AnQstWebHostBase::anQstBridge_hoverLeft, this, [this](const QString& service, const QString& member) {`);
|
|
929
|
+
lines.push(` if (service == QStringLiteral("${service.name}") && member == QStringLiteral("${member.name}")) {`);
|
|
930
|
+
lines.push(` emit ${member.name}Left();`);
|
|
931
|
+
lines.push(` }`);
|
|
932
|
+
lines.push(` });`);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
863
936
|
lines.push(` QObject::connect(this, &AnQstWebHostBase::onHostError, this, &${widgetClassName}::diagnosticsForwarded);`);
|
|
864
937
|
lines.push(` const bool rootOk = setContentRoot(QString::fromUtf8(kBootstrapContentRoot));`);
|
|
865
938
|
lines.push(` const bool bridgeOk = setBridgeObject(this, QString::fromUtf8(kBootstrapBridgeObject));`);
|
|
@@ -1172,7 +1245,24 @@ function normalizeSlashes(value) {
|
|
|
1172
1245
|
return value.split(node_path_1.default.sep).join("/");
|
|
1173
1246
|
}
|
|
1174
1247
|
function resolveActiveBuildStamp() {
|
|
1175
|
-
|
|
1248
|
+
const fromEnv = process.env.ANQST_BUILD_STAMP?.trim();
|
|
1249
|
+
if (fromEnv && fromEnv.length > 0) {
|
|
1250
|
+
return fromEnv;
|
|
1251
|
+
}
|
|
1252
|
+
const activePath = node_path_1.default.resolve(__dirname, "..", "..", ".anqstgen-version-active.json");
|
|
1253
|
+
if (!node_fs_1.default.existsSync(activePath)) {
|
|
1254
|
+
return "";
|
|
1255
|
+
}
|
|
1256
|
+
try {
|
|
1257
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(activePath, "utf8"));
|
|
1258
|
+
if (typeof parsed.active === "string" && parsed.active.trim().length > 0) {
|
|
1259
|
+
return parsed.active.trim();
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
catch {
|
|
1263
|
+
return "";
|
|
1264
|
+
}
|
|
1265
|
+
return "";
|
|
1176
1266
|
}
|
|
1177
1267
|
function withBuildStamp(relativePath, content) {
|
|
1178
1268
|
const stamp = resolveActiveBuildStamp();
|
|
@@ -1315,6 +1405,19 @@ function renderTsService(spec, serviceName) {
|
|
|
1315
1405
|
constructorBodyLines.push(` this._bridge.onOutput("${serviceName}", "${m.name}", (value) => this._${m.name}.set(value as ${tsType}));`);
|
|
1316
1406
|
}
|
|
1317
1407
|
}
|
|
1408
|
+
if (m.kind === "DropTarget" && m.payloadTypeText) {
|
|
1409
|
+
const tsType = mapTypeTextToTs(m.payloadTypeText);
|
|
1410
|
+
fieldLines.push(` private readonly _${m.name} = signal<{ payload: ${tsType}; x: number; y: number } | null>(null);`);
|
|
1411
|
+
methodLines.push(` ${m.name}(): { payload: ${tsType}; x: number; y: number } | null { return this._${m.name}(); }`);
|
|
1412
|
+
constructorBodyLines.push(` this._bridge.onDrop("${serviceName}", "${m.name}", (payload, x, y) => this._${m.name}.set({ payload: payload as ${tsType}, x, y }));`);
|
|
1413
|
+
}
|
|
1414
|
+
if (m.kind === "HoverTarget" && m.payloadTypeText) {
|
|
1415
|
+
const tsType = mapTypeTextToTs(m.payloadTypeText);
|
|
1416
|
+
fieldLines.push(` private readonly _${m.name} = signal<{ payload: ${tsType}; x: number; y: number } | null>(null);`);
|
|
1417
|
+
methodLines.push(` ${m.name}(): { payload: ${tsType}; x: number; y: number } | null { return this._${m.name}(); }`);
|
|
1418
|
+
constructorBodyLines.push(` this._bridge.onHover("${serviceName}", "${m.name}", (payload, x, y) => this._${m.name}.set({ payload: payload as ${tsType}, x, y }));`);
|
|
1419
|
+
constructorBodyLines.push(` this._bridge.onHoverLeft("${serviceName}", "${m.name}", () => this._${m.name}.set(null));`);
|
|
1420
|
+
}
|
|
1318
1421
|
}
|
|
1319
1422
|
const constructorLines = [
|
|
1320
1423
|
" constructor() {",
|
|
@@ -1366,6 +1469,14 @@ function renderTsServiceDts(spec, serviceName) {
|
|
|
1366
1469
|
setMembers.push(` ${m.name}(value: ${tsType}): void;`);
|
|
1367
1470
|
}
|
|
1368
1471
|
}
|
|
1472
|
+
if (m.kind === "DropTarget" && m.payloadTypeText) {
|
|
1473
|
+
const tsType = mapTypeTextToTs(m.payloadTypeText);
|
|
1474
|
+
classMembers.push(` ${m.name}(): { payload: ${tsType}; x: number; y: number } | null;`);
|
|
1475
|
+
}
|
|
1476
|
+
if (m.kind === "HoverTarget" && m.payloadTypeText) {
|
|
1477
|
+
const tsType = mapTypeTextToTs(m.payloadTypeText);
|
|
1478
|
+
classMembers.push(` ${m.name}(): { payload: ${tsType}; x: number; y: number } | null;`);
|
|
1479
|
+
}
|
|
1369
1480
|
}
|
|
1370
1481
|
const setInterfaceDecl = setMembers.length > 0
|
|
1371
1482
|
? `export interface ${setInterfaceName} {\n${setMembers.join("\n")}\n}`
|
|
@@ -1396,6 +1507,9 @@ type SlotHandler = (...args: unknown[]) => unknown;
|
|
|
1396
1507
|
type OutputHandler = (value: unknown) => void;
|
|
1397
1508
|
type SlotInvocationListener = (requestId: string, service: string, member: string, args: unknown[]) => void;
|
|
1398
1509
|
type OutputListener = (service: string, member: string, value: unknown) => void;
|
|
1510
|
+
type DropListener = (service: string, member: string, payload: unknown, x: number, y: number) => void;
|
|
1511
|
+
type HoverListener = (service: string, member: string, payload: unknown, x: number, y: number) => void;
|
|
1512
|
+
type HoverLeftListener = (service: string, member: string) => void;
|
|
1399
1513
|
|
|
1400
1514
|
interface HostBridgeApi {
|
|
1401
1515
|
anQstBridge_call(service: string, member: string, args: unknown[], callback: (result: unknown) => void): void;
|
|
@@ -1407,6 +1521,9 @@ interface HostBridgeApi {
|
|
|
1407
1521
|
anQstBridge_slotInvocationRequested: {
|
|
1408
1522
|
connect: (cb: (requestId: string, service: string, member: string, args: unknown[]) => void) => void;
|
|
1409
1523
|
};
|
|
1524
|
+
anQstBridge_dropReceived: { connect: (cb: (service: string, member: string, payload: unknown, x: number, y: number) => void) => void };
|
|
1525
|
+
anQstBridge_hoverUpdated: { connect: (cb: (service: string, member: string, payload: unknown, x: number, y: number) => void) => void };
|
|
1526
|
+
anQstBridge_hoverLeft: { connect: (cb: (service: string, member: string) => void) => void };
|
|
1410
1527
|
}
|
|
1411
1528
|
|
|
1412
1529
|
interface QWebChannelCtor {
|
|
@@ -1424,6 +1541,9 @@ interface BridgeAdapter {
|
|
|
1424
1541
|
resolveSlot(requestId: string, ok: boolean, payload: unknown, error: string): void;
|
|
1425
1542
|
onOutput(handler: OutputListener): void;
|
|
1426
1543
|
onSlotInvocation(handler: SlotInvocationListener): void;
|
|
1544
|
+
onDrop(handler: DropListener): void;
|
|
1545
|
+
onHover(handler: HoverListener): void;
|
|
1546
|
+
onHoverLeft(handler: HoverLeftListener): void;
|
|
1427
1547
|
}
|
|
1428
1548
|
|
|
1429
1549
|
function isBridgeCallError(value: unknown): value is {
|
|
@@ -1511,6 +1631,18 @@ class QtWebChannelAdapter implements BridgeAdapter {
|
|
|
1511
1631
|
onSlotInvocation(handler: SlotInvocationListener): void {
|
|
1512
1632
|
this.host.anQstBridge_slotInvocationRequested.connect(handler);
|
|
1513
1633
|
}
|
|
1634
|
+
|
|
1635
|
+
onDrop(handler: DropListener): void {
|
|
1636
|
+
this.host.anQstBridge_dropReceived.connect(handler);
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
onHover(handler: HoverListener): void {
|
|
1640
|
+
this.host.anQstBridge_hoverUpdated.connect(handler);
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
onHoverLeft(handler: HoverLeftListener): void {
|
|
1644
|
+
this.host.anQstBridge_hoverLeft.connect(handler);
|
|
1645
|
+
}
|
|
1514
1646
|
}
|
|
1515
1647
|
|
|
1516
1648
|
class WebSocketBridgeAdapter implements BridgeAdapter {
|
|
@@ -1523,6 +1655,9 @@ class WebSocketBridgeAdapter implements BridgeAdapter {
|
|
|
1523
1655
|
}>();
|
|
1524
1656
|
private readonly outputListeners: OutputListener[] = [];
|
|
1525
1657
|
private readonly slotListeners: SlotInvocationListener[] = [];
|
|
1658
|
+
private readonly dropListeners: DropListener[] = [];
|
|
1659
|
+
private readonly hoverListeners: HoverListener[] = [];
|
|
1660
|
+
private readonly hoverLeftListeners: HoverLeftListener[] = [];
|
|
1526
1661
|
private requestCounter = 0;
|
|
1527
1662
|
|
|
1528
1663
|
private constructor(private readonly socket: WebSocket) {
|
|
@@ -1562,6 +1697,34 @@ class WebSocketBridgeAdapter implements BridgeAdapter {
|
|
|
1562
1697
|
}
|
|
1563
1698
|
return;
|
|
1564
1699
|
}
|
|
1700
|
+
if (type === "dropReceived") {
|
|
1701
|
+
const service = String(message["service"] ?? "");
|
|
1702
|
+
const member = String(message["member"] ?? "");
|
|
1703
|
+
const x = Number(message["x"] ?? 0);
|
|
1704
|
+
const y = Number(message["y"] ?? 0);
|
|
1705
|
+
for (const listener of this.dropListeners) {
|
|
1706
|
+
listener(service, member, message["payload"], x, y);
|
|
1707
|
+
}
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
if (type === "hoverUpdated") {
|
|
1711
|
+
const service = String(message["service"] ?? "");
|
|
1712
|
+
const member = String(message["member"] ?? "");
|
|
1713
|
+
const x = Number(message["x"] ?? 0);
|
|
1714
|
+
const y = Number(message["y"] ?? 0);
|
|
1715
|
+
for (const listener of this.hoverListeners) {
|
|
1716
|
+
listener(service, member, message["payload"], x, y);
|
|
1717
|
+
}
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
if (type === "hoverLeft") {
|
|
1721
|
+
const service = String(message["service"] ?? "");
|
|
1722
|
+
const member = String(message["member"] ?? "");
|
|
1723
|
+
for (const listener of this.hoverLeftListeners) {
|
|
1724
|
+
listener(service, member);
|
|
1725
|
+
}
|
|
1726
|
+
return;
|
|
1727
|
+
}
|
|
1565
1728
|
if (type === "hostError") {
|
|
1566
1729
|
console.error("AnQst host error:", message["payload"]);
|
|
1567
1730
|
return;
|
|
@@ -1649,6 +1812,18 @@ class WebSocketBridgeAdapter implements BridgeAdapter {
|
|
|
1649
1812
|
onSlotInvocation(handler: SlotInvocationListener): void {
|
|
1650
1813
|
this.slotListeners.push(handler);
|
|
1651
1814
|
}
|
|
1815
|
+
|
|
1816
|
+
onDrop(handler: DropListener): void {
|
|
1817
|
+
this.dropListeners.push(handler);
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
onHover(handler: HoverListener): void {
|
|
1821
|
+
this.hoverListeners.push(handler);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
onHoverLeft(handler: HoverLeftListener): void {
|
|
1825
|
+
this.hoverLeftListeners.push(handler);
|
|
1826
|
+
}
|
|
1652
1827
|
}
|
|
1653
1828
|
|
|
1654
1829
|
@Injectable({ providedIn: "root" })
|
|
@@ -1656,6 +1831,9 @@ class AnQstBridgeRuntime {
|
|
|
1656
1831
|
private adapter: BridgeAdapter | null = null;
|
|
1657
1832
|
private readonly slotHandlers = new Map<string, SlotHandler>();
|
|
1658
1833
|
private readonly outputHandlers = new Map<string, OutputHandler[]>();
|
|
1834
|
+
private readonly dropHandlers = new Map<string, ((payload: unknown, x: number, y: number) => void)[]>();
|
|
1835
|
+
private readonly hoverHandlers = new Map<string, ((payload: unknown, x: number, y: number) => void)[]>();
|
|
1836
|
+
private readonly hoverLeftHandlers = new Map<string, (() => void)[]>();
|
|
1659
1837
|
private readonly startup = this.init();
|
|
1660
1838
|
|
|
1661
1839
|
async ready(): Promise<void> {
|
|
@@ -1706,6 +1884,27 @@ class AnQstBridgeRuntime {
|
|
|
1706
1884
|
this.outputHandlers.set(key, existing);
|
|
1707
1885
|
}
|
|
1708
1886
|
|
|
1887
|
+
onDrop(service: string, member: string, handler: (payload: unknown, x: number, y: number) => void): void {
|
|
1888
|
+
const key = this.key(service, member);
|
|
1889
|
+
const existing = this.dropHandlers.get(key) ?? [];
|
|
1890
|
+
existing.push(handler);
|
|
1891
|
+
this.dropHandlers.set(key, existing);
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
onHover(service: string, member: string, handler: (payload: unknown, x: number, y: number) => void): void {
|
|
1895
|
+
const key = this.key(service, member);
|
|
1896
|
+
const existing = this.hoverHandlers.get(key) ?? [];
|
|
1897
|
+
existing.push(handler);
|
|
1898
|
+
this.hoverHandlers.set(key, existing);
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
onHoverLeft(service: string, member: string, handler: () => void): void {
|
|
1902
|
+
const key = this.key(service, member);
|
|
1903
|
+
const existing = this.hoverLeftHandlers.get(key) ?? [];
|
|
1904
|
+
existing.push(handler);
|
|
1905
|
+
this.hoverLeftHandlers.set(key, existing);
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1709
1908
|
private requireAdapterSync(): BridgeAdapter {
|
|
1710
1909
|
if (this.adapter === null) {
|
|
1711
1910
|
throw new Error("AnQst bridge is not ready.");
|
|
@@ -1751,6 +1950,24 @@ class AnQstBridgeRuntime {
|
|
|
1751
1950
|
this.adapter!.resolveSlot(requestId, false, undefined, message);
|
|
1752
1951
|
}
|
|
1753
1952
|
});
|
|
1953
|
+
this.adapter.onDrop((service, member, payload, x, y) => {
|
|
1954
|
+
const key = this.key(service, member);
|
|
1955
|
+
for (const handler of this.dropHandlers.get(key) ?? []) {
|
|
1956
|
+
handler(payload, x, y);
|
|
1957
|
+
}
|
|
1958
|
+
});
|
|
1959
|
+
this.adapter.onHover((service, member, payload, x, y) => {
|
|
1960
|
+
const key = this.key(service, member);
|
|
1961
|
+
for (const handler of this.hoverHandlers.get(key) ?? []) {
|
|
1962
|
+
handler(payload, x, y);
|
|
1963
|
+
}
|
|
1964
|
+
});
|
|
1965
|
+
this.adapter.onHoverLeft((service, member) => {
|
|
1966
|
+
const key = this.key(service, member);
|
|
1967
|
+
for (const handler of this.hoverLeftHandlers.get(key) ?? []) {
|
|
1968
|
+
handler();
|
|
1969
|
+
}
|
|
1970
|
+
});
|
|
1754
1971
|
for (const key of this.slotHandlers.keys()) {
|
|
1755
1972
|
const parts = key.split("::");
|
|
1756
1973
|
if (parts.length === 2) {
|
|
@@ -2790,6 +3007,7 @@ set(CMAKE_AUTOUIC ON)
|
|
|
2790
3007
|
set(CMAKE_AUTORCC ON)
|
|
2791
3008
|
|
|
2792
3009
|
find_program(ANQST_NPM_EXECUTABLE npm REQUIRED)
|
|
3010
|
+
find_program(ANQST_NPX_EXECUTABLE npx REQUIRED)
|
|
2793
3011
|
|
|
2794
3012
|
add_custom_command(
|
|
2795
3013
|
OUTPUT
|
|
@@ -2801,7 +3019,7 @@ add_custom_command(
|
|
|
2801
3019
|
"\${${generatedIncludeVar}}/${widgetName}Types.h"
|
|
2802
3020
|
"\${${generatedRootVar}}/webapp/index.html"
|
|
2803
3021
|
COMMAND "\${ANQST_NPM_EXECUTABLE}" install
|
|
2804
|
-
COMMAND "\${
|
|
3022
|
+
COMMAND "\${ANQST_NPX_EXECUTABLE}" anqst build
|
|
2805
3023
|
WORKING_DIRECTORY "\${${projectRootVar}}"
|
|
2806
3024
|
COMMENT "Generating AnQst widget library (${widgetTarget}) from Angular project"
|
|
2807
3025
|
VERBATIM
|
|
@@ -3029,7 +3247,7 @@ function renderQtDesignerPluginCpp(widgetName, widgetCategory, hasIcon) {
|
|
|
3029
3247
|
#include <QObject>
|
|
3030
3248
|
#include <QString>
|
|
3031
3249
|
#include <QWidget>
|
|
3032
|
-
#include "
|
|
3250
|
+
#include "${widgetName}.h"
|
|
3033
3251
|
|
|
3034
3252
|
class ${pluginClass} final : public QObject, public QDesignerCustomWidgetInterface {
|
|
3035
3253
|
Q_OBJECT
|
|
@@ -3045,9 +3263,10 @@ public:
|
|
|
3045
3263
|
QString toolTip() const override { return QStringLiteral("${widgetName} generated by AnQst."); }
|
|
3046
3264
|
QString whatsThis() const override { return QStringLiteral("${widgetName} generated by AnQst."); }
|
|
3047
3265
|
bool isContainer() const override { return false; }
|
|
3048
|
-
QString includeFile() const override { return QStringLiteral("
|
|
3266
|
+
QString includeFile() const override { return QStringLiteral("${widgetName}.h"); }
|
|
3049
3267
|
QWidget* createWidget(QWidget* parent) override {
|
|
3050
3268
|
auto* widget = new ${widgetClass}(parent);
|
|
3269
|
+
widget->setMinimumHeight(128);
|
|
3051
3270
|
widget->setProperty("anqstDesignerContext", true);
|
|
3052
3271
|
return widget;
|
|
3053
3272
|
}
|
|
@@ -3058,6 +3277,12 @@ public:
|
|
|
3058
3277
|
return QStringLiteral(
|
|
3059
3278
|
"<ui language=\\"c++\\">\\n"
|
|
3060
3279
|
" <widget class=\\"${widgetClass}\\" name=\\"${widgetName.toLowerCase()}\\">\\n"
|
|
3280
|
+
" <property name=\\"minimumSize\\">\\n"
|
|
3281
|
+
" <size>\\n"
|
|
3282
|
+
" <width>0</width>\\n"
|
|
3283
|
+
" <height>128</height>\\n"
|
|
3284
|
+
" </size>\\n"
|
|
3285
|
+
" </property>\\n"
|
|
3061
3286
|
" </widget>\\n"
|
|
3062
3287
|
"</ui>\\n");
|
|
3063
3288
|
}
|