diff --git a/bin/php-qml-init b/bin/php-qml-init index 21cdb1c..64aafdf 100755 --- a/bin/php-qml-init +++ b/bin/php-qml-init @@ -151,6 +151,14 @@ sed -i \ -e "s|\$(BUILD_DIR)/skeleton|\$(BUILD_DIR)/$NAME|g" \ "$TARGET/Makefile" +# .vscode/launch.json: binary path + config label both mention `skeleton`. +if [ -f "$TARGET/.vscode/launch.json" ]; then + sed -i \ + -e "s|build/qml/skeleton|build/qml/$NAME|g" \ + -e "s|Run skeleton (Qt host)|Run $NAME (Qt host)|g" \ + "$TARGET/.vscode/launch.json" +fi + # ── Path-repo: absolute reference, or vendor a copy ────────────────── COMPOSER_JSON="$TARGET/symfony/composer.json" [ -f "$COMPOSER_JSON" ] || die "skeleton missing symfony/composer.json (corrupt copy?)" diff --git a/examples/todo/.idea/.gitignore b/examples/todo/.idea/.gitignore new file mode 100644 index 0000000..57d484e --- /dev/null +++ b/examples/todo/.idea/.gitignore @@ -0,0 +1,5 @@ +# Track only the shared run configs; ignore per-user IDE state. +* +!.gitignore +!runConfigurations/ +!runConfigurations/*.xml diff --git a/examples/todo/.idea/runConfigurations/make_appimage.xml b/examples/todo/.idea/runConfigurations/make_appimage.xml new file mode 100644 index 0000000..eb46fd6 --- /dev/null +++ b/examples/todo/.idea/runConfigurations/make_appimage.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/todo/.idea/runConfigurations/make_dev.xml b/examples/todo/.idea/runConfigurations/make_dev.xml new file mode 100644 index 0000000..c732a41 --- /dev/null +++ b/examples/todo/.idea/runConfigurations/make_dev.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/todo/.idea/runConfigurations/make_doctor.xml b/examples/todo/.idea/runConfigurations/make_doctor.xml new file mode 100644 index 0000000..214305d --- /dev/null +++ b/examples/todo/.idea/runConfigurations/make_doctor.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/todo/.idea/runConfigurations/make_quality.xml b/examples/todo/.idea/runConfigurations/make_quality.xml new file mode 100644 index 0000000..91dbe3b --- /dev/null +++ b/examples/todo/.idea/runConfigurations/make_quality.xml @@ -0,0 +1,17 @@ + + + + diff --git a/examples/todo/.vscode/launch.json b/examples/todo/.vscode/launch.json new file mode 100644 index 0000000..114f84e --- /dev/null +++ b/examples/todo/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for Xdebug (Symfony / FrankenPHP)", + "type": "php", + "request": "launch", + "port": 9003, + "pathMappings": { + "${workspaceFolder}/symfony": "${workspaceFolder}/symfony" + }, + "log": false + }, + { + "name": "Run todo (Qt host)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/qml/todo", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { "name": "BRIDGE_URL", "value": "http://127.0.0.1:8765" }, + { "name": "BRIDGE_TOKEN", "value": "devtoken" } + ], + "preLaunchTask": "make build", + "MIMode": "gdb", + "linux": { "MIMode": "gdb" }, + "osx": { "MIMode": "lldb" } + } + ], + "compounds": [ + { + "name": "Dev: Xdebug + Qt host", + "configurations": [ + "Listen for Xdebug (Symfony / FrankenPHP)", + "Run todo (Qt host)" + ] + } + ] +} diff --git a/examples/todo/.vscode/settings.json b/examples/todo/.vscode/settings.json new file mode 100644 index 0000000..c7da623 --- /dev/null +++ b/examples/todo/.vscode/settings.json @@ -0,0 +1,23 @@ +{ + "files.exclude": { + "**/build": true, + "**/.qt": true, + "**/.rcc": true, + "**/var/cache": true, + "**/var/log": true, + "**/vendor": true, + "**/packaging/linux/tools": true + }, + "search.exclude": { + "**/build": true, + "**/vendor": true, + "**/.qt": true, + "**/.rcc": true, + "**/packaging/linux/tools": true + }, + "[php]": { "editor.tabSize": 4 }, + "[qml]": { "editor.tabSize": 4 }, + "[cpp]": { "editor.tabSize": 4 }, + "intelephense.environment.phpVersion": "8.4.0", + "qt-qml.qmlls.useQmlImportPathEnvVar": true +} diff --git a/examples/todo/.vscode/tasks.json b/examples/todo/.vscode/tasks.json new file mode 100644 index 0000000..5109c57 --- /dev/null +++ b/examples/todo/.vscode/tasks.json @@ -0,0 +1,50 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "make build", + "type": "shell", + "command": "make", + "args": ["build"], + "group": { "kind": "build", "isDefault": true }, + "problemMatcher": ["$gcc"] + }, + { + "label": "make dev", + "type": "shell", + "command": "make", + "args": ["dev"], + "isBackground": true, + "presentation": { "reveal": "always", "panel": "dedicated" }, + "problemMatcher": [] + }, + { + "label": "make doctor", + "type": "shell", + "command": "make", + "args": ["doctor"], + "problemMatcher": [] + }, + { + "label": "make quality", + "type": "shell", + "command": "make", + "args": ["quality"], + "problemMatcher": ["$gcc"] + }, + { + "label": "make integration", + "type": "shell", + "command": "make", + "args": ["integration"], + "problemMatcher": [] + }, + { + "label": "make appimage", + "type": "shell", + "command": "make", + "args": ["appimage"], + "problemMatcher": ["$gcc"] + } + ] +} diff --git a/framework/skeleton/.idea/.gitignore b/framework/skeleton/.idea/.gitignore new file mode 100644 index 0000000..57d484e --- /dev/null +++ b/framework/skeleton/.idea/.gitignore @@ -0,0 +1,5 @@ +# Track only the shared run configs; ignore per-user IDE state. +* +!.gitignore +!runConfigurations/ +!runConfigurations/*.xml diff --git a/framework/skeleton/.idea/runConfigurations/make_dev.xml b/framework/skeleton/.idea/runConfigurations/make_dev.xml new file mode 100644 index 0000000..c732a41 --- /dev/null +++ b/framework/skeleton/.idea/runConfigurations/make_dev.xml @@ -0,0 +1,17 @@ + + + + diff --git a/framework/skeleton/.idea/runConfigurations/make_doctor.xml b/framework/skeleton/.idea/runConfigurations/make_doctor.xml new file mode 100644 index 0000000..214305d --- /dev/null +++ b/framework/skeleton/.idea/runConfigurations/make_doctor.xml @@ -0,0 +1,17 @@ + + + + diff --git a/framework/skeleton/.idea/runConfigurations/make_quality.xml b/framework/skeleton/.idea/runConfigurations/make_quality.xml new file mode 100644 index 0000000..91dbe3b --- /dev/null +++ b/framework/skeleton/.idea/runConfigurations/make_quality.xml @@ -0,0 +1,17 @@ + + + + diff --git a/framework/skeleton/.vscode/launch.json b/framework/skeleton/.vscode/launch.json new file mode 100644 index 0000000..bbd8961 --- /dev/null +++ b/framework/skeleton/.vscode/launch.json @@ -0,0 +1,41 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for Xdebug (Symfony / FrankenPHP)", + "type": "php", + "request": "launch", + "port": 9003, + "pathMappings": { + "${workspaceFolder}/symfony": "${workspaceFolder}/symfony" + }, + "log": false + }, + { + "name": "Run skeleton (Qt host)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/qml/skeleton", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { "name": "BRIDGE_URL", "value": "http://127.0.0.1:8765" }, + { "name": "BRIDGE_TOKEN", "value": "devtoken" } + ], + "preLaunchTask": "make build", + "MIMode": "gdb", + "linux": { "MIMode": "gdb" }, + "osx": { "MIMode": "lldb" } + } + ], + "compounds": [ + { + "name": "Dev: Xdebug + Qt host", + "configurations": [ + "Listen for Xdebug (Symfony / FrankenPHP)", + "Run skeleton (Qt host)" + ] + } + ] +} diff --git a/framework/skeleton/.vscode/settings.json b/framework/skeleton/.vscode/settings.json new file mode 100644 index 0000000..fa8c7c4 --- /dev/null +++ b/framework/skeleton/.vscode/settings.json @@ -0,0 +1,22 @@ +{ + "files.exclude": { + "**/build": true, + "**/.qt": true, + "**/.rcc": true, + "**/var/cache": true, + "**/var/log": true, + "**/vendor": true + }, + "search.exclude": { + "**/build": true, + "**/vendor": true, + "**/.qt": true, + "**/.rcc": true + }, + "[php]": { "editor.tabSize": 4 }, + "[qml]": { "editor.tabSize": 4 }, + "[cpp]": { "editor.tabSize": 4 }, + "intelephense.environment.phpVersion": "8.4.0", + "intelephense.files.associations": ["*.php", "*.phtml"], + "qt-qml.qmlls.useQmlImportPathEnvVar": true +} diff --git a/framework/skeleton/.vscode/tasks.json b/framework/skeleton/.vscode/tasks.json new file mode 100644 index 0000000..b6a63f4 --- /dev/null +++ b/framework/skeleton/.vscode/tasks.json @@ -0,0 +1,36 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "make build", + "type": "shell", + "command": "make", + "args": ["build"], + "group": { "kind": "build", "isDefault": true }, + "problemMatcher": ["$gcc"] + }, + { + "label": "make dev", + "type": "shell", + "command": "make", + "args": ["dev"], + "isBackground": true, + "presentation": { "reveal": "always", "panel": "dedicated" }, + "problemMatcher": [] + }, + { + "label": "make doctor", + "type": "shell", + "command": "make", + "args": ["doctor"], + "problemMatcher": [] + }, + { + "label": "make quality", + "type": "shell", + "command": "make", + "args": ["quality"], + "problemMatcher": ["$gcc"] + } + ] +} diff --git a/framework/skeleton/README.md b/framework/skeleton/README.md index e2525bf..5e063c7 100644 --- a/framework/skeleton/README.md +++ b/framework/skeleton/README.md @@ -6,7 +6,7 @@ The framework's reference application: a minimal Symfony backend plus a Qt/QML h - Linux (other platforms land in Phase 4) - Qt 6.5+ dev packages (`qt6-base-devel`, `qt6-declarative-devel`, `qt6-quickcontrols2-devel`, `qt6-tools-devel`), CMake, gcc-c++ -- PHP 8.3+ +- PHP 8.4+ (Symfony 8 enforces this) - [FrankenPHP](https://frankenphp.dev/) on PATH (or set `FRANKENPHP=/path/to/frankenphp`) - Composer @@ -63,6 +63,50 @@ curl -X POST http://127.0.0.1:8765/api/todos \ The Mercure SSE stream receives a `correlationKey: my-key-1` envelope which the Qt host's `ReactiveListModel` matches against any in-flight optimistic mutation (PLAN.md §5). +## Hot reload + +Both halves of the app reload without re-running `make dev`. + +### PHP-side (Symfony / FrankenPHP) + +`make dev` runs `frankenphp run --watch` (see `Caddyfile` and `scripts/dev.sh`). FrankenPHP rebuilds the worker on any change under `symfony/` — controllers, services, entities, templates, configuration. Just save the file; the next request through the Qt host hits the new code. There is no opcache to clear and no service to restart. + +If you change something Doctrine-mapped, run a fresh migration in another terminal: + +```bash +cd symfony +bin/console make:migration +bin/console doctrine:migrations:migrate -n +``` + +The Qt host stays up across all of this. + +### QML-side + +The Qt host loads QML from a compiled-in resource bundle, so saving a `.qml` file does **not** flip the running window automatically. Three workflows that do: + +- **Qt Creator → File → Reload** (or `Ctrl+R` with focus on a QML file). Rebuilds the QML cache and reloads the window in place. +- **`qmlls` live preview** — the QML language server bundled with Qt 6.5+ runs a live preview connected to your editor (VSCode + the Qt extension, neovim, helix). Edits show up instantly in the preview window without rebuilding. +- **Run from source** — start the host with `QT_QUICK_CONTROLS_CONF=` and `QML_IMPORT_TRACE=1` set, and pass `-DQT_QML_DEBUG` so the running engine accepts a hot-reload connection from Qt Creator. PLAN.md §6 captures the long-term plan to gate this behind `BRIDGE_DEV=1`. + +For most edits, Qt Creator's *Reload* is the lowest-friction option. The `.qmlls.ini` file (auto-generated when `qmlls` first runs) configures completion + live preview against this project's QML import paths. + +### Editor configs + +Both `.vscode/` and `.idea/runConfigurations/` ship with the skeleton. + +VSCode (`.vscode/launch.json`): + +- **Listen for Xdebug** — attaches the debugger on port 9003 once you set `XDEBUG_MODE=debug` for the FrankenPHP child (e.g. `XDEBUG_MODE=debug make dev`). +- **Run skeleton (Qt host)** — gdb-launches the built binary with `BRIDGE_URL=http://127.0.0.1:8765` so it talks to the dev mode FrankenPHP started elsewhere by `make dev`. +- **Compound: Dev: Xdebug + Qt host** — runs both at once. + +PhpStorm (`.idea/runConfigurations/`): `make dev`, `make doctor`, `make quality` shell run configs. PHP debugging is via the toolbar's **Start Listening for PHP Debug Connections** toggle (PhpStorm's Xdebug listener is global, not per-project). + +### Dev console + +`Ctrl+`` toggles an in-window console showing the bundled FrankenPHP child's stdout + stderr (PLAN.md §8). It's a passive ring buffer (~500 lines) — opening it has no IPC cost. Use it when you don't have a separate terminal to read the dev log. + ## Quality checks ```bash