|
| 1 | +# Strategy & long-term improvements — 2026-07 GUI base audit |
| 2 | + |
| 3 | +The analysis behind the [issue tracker](../issues/index.md) and |
| 4 | +[roadmap](../roadmap/index.md): what state the base is in, where it |
| 5 | +should go, and the long-term improvements beyond the immediate fixes. |
| 6 | +Written to brief the owner and the implementing agents on _why_ the plan |
| 7 | +is ordered as it is. |
| 8 | + |
| 9 | +## Executive summary |
| 10 | + |
| 11 | +`gui-components` (`EasyApplication`) is a mature-looking Qt/QML |
| 12 | +component library with a genuinely good **visual design system** |
| 13 | +(coherent Style tokens, a clean Elements→Components layering, theming |
| 14 | +with animated transitions, a broad control set). It works today as the |
| 15 | +desktop-Python GUI base for `easydiffractionbeta`. |
| 16 | + |
| 17 | +But measured against the goal — _a solid, robust, sustainable, |
| 18 | +maintainable base for a **new** product (`edi`) shipping on **desktop |
| 19 | +and web/WASM**_, built by **less-advanced agents** — it has three |
| 20 | +structural gaps: |
| 21 | + |
| 22 | +1. **The web/WASM target is broken and architecturally blocked.** The |
| 23 | + C++ build's resource manifest rotted at the |
| 24 | + `EasyApp`→`EasyApplication` rename (I-0001); the charts/report need |
| 25 | + `QtWebEngine`, which does not exist on WASM (I-0002); settings |
| 26 | + persistence throws in the browser (I-0003); and the WASM CI never |
| 27 | + runs, so none of this was caught (I-0005). |
| 28 | +2. **It is entangled with EasyDiffraction.** App page names, the product |
| 29 | + name, an auto-updater pointing at EasyDiffraction's servers, and a |
| 30 | + `QtTest`-based tutorial harness are compiled into the "generic" |
| 31 | + library (I-0006, I-0007, I-0012). A second product cannot adopt it |
| 32 | + cleanly. |
| 33 | +3. **The engineering foundation is classic-only and ungated.** No |
| 34 | + CMake/`qt_add_qml_module`, no QML linting/formatting, no real tests |
| 35 | + (all `test_dummy.py`), reliance on private Qt internals in 33 files, |
| 36 | + and a repo scaffolded from a generic _Python-library_ template that |
| 37 | + checks the thin Python shell while the thick QML core goes ungated |
| 38 | + (I-0004, I-0009, I-0010, I-0011, I-0018, I-0026). |
| 39 | + |
| 40 | +None of this requires a rewrite. It requires **hardening + decoupling** |
| 41 | +on top of the good design system, in a deliberate order: unbreak the web |
| 42 | +target on a drift-proof build, decouple from EasyDiffraction, then gate |
| 43 | +quality so the hardening sticks. |
| 44 | + |
| 45 | +## The three-phase plan (and why this order) |
| 46 | + |
| 47 | +- **Phase I — Unbreak & stabilise (M01–M03).** Restore the web target on |
| 48 | + a CMake module build that _cannot_ drift (M01), make it actually |
| 49 | + render and persist on WASM (M02), and decouple it from EasyDiffraction |
| 50 | + (M03). After Phase I, `edi` can start building its surfaces on a base |
| 51 | + that builds everywhere and isn't someone else's app. M01 and M03 |
| 52 | + parallelise (build vs decoupling). |
| 53 | +- **Phase II — Sustainable engineering (M04–M06).** Gate the _actual |
| 54 | + product_: `qmllint`, `qmlformat`, Qt Quick Test required in CI (M04); |
| 55 | + retire private-API reliance and modernise imports (M05); define a real |
| 56 | + distribution model and prune deps/fonts (M06). This is what makes the |
| 57 | + base _maintainable_ by many hands. |
| 58 | +- **Phase III — Solid base for edi (M07–M09).** i18n/a11y (M07), |
| 59 | + documentation + a component gallery + an enforced style guide (M08), |
| 60 | + and the `edi` handoff seam + semver (M09). This is what makes the base |
| 61 | + _followable_ by less-advanced agents. |
| 62 | + |
| 63 | +## Long-term improvements (beyond the issue fixes) |
| 64 | + |
| 65 | +These are not individual bugs; they are the direction that keeps the |
| 66 | +base healthy for years. |
| 67 | + |
| 68 | +1. **One build system, generated artifacts.** Standardise on CMake |
| 69 | + `qt_add_qml_module` as the source of truth for module identity, |
| 70 | + resources, and compiled QML. Hand-maintained `.qrc`/`.pro` is how |
| 71 | + I-0001 happened; generated manifests make that class of bug |
| 72 | + impossible. Keep the PySide interpreted path as a thin consumer of |
| 73 | + the same source tree (dual distribution — see |
| 74 | + [architecture-target.md](architecture-target.md)). |
| 75 | +2. **A stable façade layer for anything platform-divergent.** Charts, |
| 76 | + settings, file dialogs, clipboard, update-check, and audio behave |
| 77 | + differently (or don't exist) on WASM. Put each behind a small QML |
| 78 | + façade with a fixed property API and a target-aware backend, so app |
| 79 | + code is written once and the platform difference lives in one file. |
| 80 | + (M02 does charts + settings; extend the pattern.) |
| 81 | +3. **Decoupling by construction, enforced.** The library must contain |
| 82 | + **zero** product literals and **zero** named app pages. Make that a |
| 83 | + CI grep gate, not a convention — so it can't regress as new agents |
| 84 | + contribute. Everything app-specific enters via injected data |
| 85 | + (`ApplicationInfo`, a page model, an optional update service). |
| 86 | +4. **Compiled, type-safe QML.** Move from context-property injection |
| 87 | + (`typeof pyX !== "undefined"` in `Vars.qml`) and `var` properties |
| 88 | + toward `required property`, typed properties, and |
| 89 | + `QML_ELEMENT`/singleton registration, so `qmlsc` can compile QML to |
| 90 | + C++ (startup + WASM wins) and `qmllint` can actually check it. |
| 91 | +5. **A test pyramid whose base is a smoke-load.** The single |
| 92 | + highest-value test is "does every component instantiate without a QML |
| 93 | + error", run headless in CI on desktop and WASM. It would have caught |
| 94 | + I-0001/I-0013/I-0020 for near-zero cost. Build up from there |
| 95 | + (interactive Qt Quick Test, Python backend units, a visual gallery). |
| 96 | +6. **The library documents itself.** A component gallery that renders |
| 97 | + every Element/Component (in light+dark) doubles as living |
| 98 | + documentation _and_ a visual smoke test. Junior agents building `edi` |
| 99 | + should be able to browse "what exists and how to use it" without |
| 100 | + reading source. |
| 101 | +7. **Design tokens as a first-class, exportable system.** The Style |
| 102 | + singletons are already a de facto design system; formalise them |
| 103 | + (documented token names, light/dark, the sizing scale) so they can be |
| 104 | + shared with non-Qt surfaces (web docs, marketing) and evolved |
| 105 | + deliberately. |
| 106 | +8. **Accessibility and i18n as defaults, not retrofits.** Establish |
| 107 | + `qsTr()`-everywhere and `Accessible` roles as conventions now, while |
| 108 | + the base is ~90 components, rather than across a grown `edi`. |
| 109 | +9. **Version and support the base like a product.** Semver, a changelog, |
| 110 | + and a documented support window for Qt versions — so `edi` can pin a |
| 111 | + known-good base and upgrade deliberately. |
| 112 | + |
| 113 | +## What to preserve (do not "improve" away) |
| 114 | + |
| 115 | +- The **Style token system** (`Colors`/`Sizes`/`Fonts`/`Times`) and the |
| 116 | + dark/light palette — it is hand-tuned and coherent. |
| 117 | +- The **Elements (primitive) → Components (composed)** layering — it is |
| 118 | + the right shape; just rename the ambiguous base window (I-0028) and |
| 119 | + document it (I-0030). |
| 120 | +- The **animated theming/translation transitions** |
| 121 | + (`Behavior on color { ThemeChange {} }`) — a nice touch that gives the |
| 122 | + app polish; keep them target-aware. |
| 123 | +- The **breadth of controls** — the library already covers most of what |
| 124 | + an analysis app needs. |
| 125 | + |
| 126 | +## Risks & mitigations |
| 127 | + |
| 128 | +- **Chart migration is the biggest single effort** (10 files + report, |
| 129 | + plus a library choice). De-risk by validating the ratified library |
| 130 | + choice (Decision A: QtGraphs) on a measured 1-D prototype in M02-T1 |
| 131 | + before doing 2-D/3-D. |
| 132 | +- **CMake migration could disturb the working PySide path.** Mitigate by |
| 133 | + keeping the raw `.qml` in place and treating CMake as an additional |
| 134 | + consumer (dual build), landing modules incrementally (M01-T1 beachhead |
| 135 | + first). |
| 136 | +- **Decoupling churns many files.** Mitigate with the smoke-load test |
| 137 | + (M04-T2) landed early so the churn is caught, and CI grep gates so |
| 138 | + decoupling can't silently regress. |
| 139 | +- **Less-advanced implementing agents.** Mitigate with explicit task |
| 140 | + packets (done), an enforced style guide (I-0030/M08), and the |
| 141 | + [writing-for-junior-agents](../process/writing-for-junior-agents.md) |
| 142 | + conventions. |
| 143 | + |
| 144 | +## Open decisions (ratify before Phase I code) |
| 145 | + |
| 146 | +See [decisions-to-confirm.md](decisions-to-confirm.md): the chart |
| 147 | +library, the build/distribution model, the template framing, and how far |
| 148 | +to split app-specific screens out of the library. These change _how_ |
| 149 | +Phase I is executed and are cheap to decide now, expensive to reverse |
| 150 | +later. |
0 commit comments