refactor(instellingen): de opslagpanelen uit de gedeelde part-scope (#631) #716

Merged
brenno merged 7 commits from refactor/settings-panelen-uit-gedeelde-scope-631 into main 2026-07-23 09:29:34 +00:00
Owner

Sluit het eerste, scherpste deel van #631: de panelen die om inloggegevens vragen leven niet meer in de gedeelde part-scope van _SettingsDialogState.

Wat er mis was. Alles onder lib/widgets/dialogs/parts/ deelt één privé-scope met de venstertoestand. Zevenentwintig bestanden, twintig onderwerpen, en elk daarvan kan syntactisch bij élk veld van de andere zesentwintig — een wijziging aan het CVE-paneel raakt technisch gesproken de S3-inloggegevens. Geen poort ziet dat en de compiler geeft geen signaal.

Wat er nu staat. lib/widgets/dialogs/settings/, buiten die scope:

  • WebdavPanel, S3Panel, GitPanel — gewone widgets met een expliciete API: het formulier dat ze bewerken, een ConfirmCertificate-callback, en een onChanged die het venster vertelt dat de statusregel achter de verbindingsnaam verouderd is;
  • de vier formulierklassen (WebdavForm, S3Form, GitForm, AiForm) en KeychainSecret;
  • SettingsSectionTitle + SettingsSectionAnchors, SettingsTextField, SettingsSecretField.

_SettingsDialogState gaat van 7.295 naar 6.043 regels; de ratchet in check_conventions.dart is per stap meegezakt.

Waarom dit meer is dan verplaatsen. Elk paneel is nu te tekenen en te toetsen zónder de dialoog te openen, en die tests staan erbij. Ze bewaken het geval dat in alle drie hetzelfde stil misgaat: een instelling wijzigen die de vorige verbindingstest ongeldig maakt zonder de groene vink weg te halen — dan meldt het paneel "verbinding gelukt" over een verbinding die het niet geprobeerd heeft.

Wat de verhuizing zelf aan het licht bracht. Twaalf zichtbare hints liepen buiten l10n.d() om. Niet nieuw: _webdavField was een extension-methode, en daar kijkt de datastroomanalyse van check_hardcoded_text niet doorheen. Zodra het een gewone widget werd, vielen ze alle twaalf om. Tien zijn identifiers en staan in unchangedInAllLanguages, twee zijn in 31 talen vertaald, en de twee Nederlandse voorbeelddomeinen zijn example.com geworden in plaats van eenendertig keer een adres dat niet bestaat.

Nog open in #631: het AI-tabblad zelf. Dat is geen paneel in de verbindingenlijst maar een tabblad met eigen init en opslag; AiForm is er wel al uit.

Poort: make check groen (5.858 tests), make test-golden groen (33), make sbom-verify schoon. Herbaseerd op main#715 — twee conflicten, beide dezelfde soort: main maakte in dezelfde regels de contrastkleur mode-afhankelijk (accentaccentFg, tealtealFg) terwijl deze tak de code verplaatste. Beide keren is de verplaatsing mét de contrastfix overgenomen.

Closes #631

Sluit het eerste, scherpste deel van #631: de panelen die om inloggegevens vragen leven niet meer in de gedeelde `part`-scope van `_SettingsDialogState`. **Wat er mis was.** Alles onder `lib/widgets/dialogs/parts/` deelt één privé-scope met de venstertoestand. Zevenentwintig bestanden, twintig onderwerpen, en elk daarvan kan syntactisch bij élk veld van de andere zesentwintig — een wijziging aan het CVE-paneel raakt technisch gesproken de S3-inloggegevens. Geen poort ziet dat en de compiler geeft geen signaal. **Wat er nu staat.** `lib/widgets/dialogs/settings/`, buiten die scope: - `WebdavPanel`, `S3Panel`, `GitPanel` — gewone widgets met een expliciete API: het formulier dat ze bewerken, een `ConfirmCertificate`-callback, en een `onChanged` die het venster vertelt dat de statusregel achter de verbindingsnaam verouderd is; - de vier formulierklassen (`WebdavForm`, `S3Form`, `GitForm`, `AiForm`) en `KeychainSecret`; - `SettingsSectionTitle` + `SettingsSectionAnchors`, `SettingsTextField`, `SettingsSecretField`. `_SettingsDialogState` gaat van **7.295 naar 6.043 regels**; de ratchet in `check_conventions.dart` is per stap meegezakt. **Waarom dit meer is dan verplaatsen.** Elk paneel is nu te tekenen en te toetsen zónder de dialoog te openen, en die tests staan erbij. Ze bewaken het geval dat in alle drie hetzelfde stil misgaat: een instelling wijzigen die de vorige verbindingstest ongeldig maakt zonder de groene vink weg te halen — dan meldt het paneel "verbinding gelukt" over een verbinding die het niet geprobeerd heeft. **Wat de verhuizing zelf aan het licht bracht.** Twaalf zichtbare hints liepen buiten `l10n.d()` om. Niet nieuw: `_webdavField` was een `extension`-methode, en daar kijkt de datastroomanalyse van `check_hardcoded_text` niet doorheen. Zodra het een gewone widget werd, vielen ze alle twaalf om. Tien zijn identifiers en staan in `unchangedInAllLanguages`, twee zijn in 31 talen vertaald, en de twee Nederlandse voorbeelddomeinen zijn `example.com` geworden in plaats van eenendertig keer een adres dat niet bestaat. **Nog open in #631:** het AI-*tabblad* zelf. Dat is geen paneel in de verbindingenlijst maar een tabblad met eigen init en opslag; `AiForm` is er wel al uit. Poort: `make check` groen (5.858 tests), `make test-golden` groen (33), `make sbom-verify` schoon. Herbaseerd op `main` ná #715 — twee conflicten, beide dezelfde soort: main maakte in dezelfde regels de contrastkleur mode-afhankelijk (`accent` → `accentFg`, `teal` → `tealFg`) terwijl deze tak de code verplaatste. Beide keren is de verplaatsing mét de contrastfix overgenomen. Closes #631
De sectiekop, het invulveld en het geheimveld waren `extension`-methoden op
_SettingsDialogState. Elk paneel dat ze nodig had, moest dus in de gedeelde
`part`-scope leven — en kon daarmee bij élk veld van de andere zesentwintig
parts. Dat is de koppeling waar #631 over gaat: geen poort ziet hem en de
compiler geeft geen signaal.

Ze staan nu als gewone widgets in lib/widgets/dialogs/settings/. De sectiekop
haalt zijn anker en het oplichten uit een SettingsSectionAnchors-InheritedWidget
in plaats van uit de venstertoestand, zodat een paneel niet hoeft te weten dát
er gezocht kan worden.

De bestaande helpers blijven staan als eenregelige doorgeefluiken: dit is de
voorbereiding, niet de verhuizing. De panelen zelf volgen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Het paneel stond als `extension _SettingsS3 on _SettingsDialogState` in de
gedeelde part-scope: het kon syntactisch bij élk veld van de zesentwintig
andere parts, inclusief het wachtwoord van WebDAV, het token van git en de
API-sleutel van de AI-backend. Dat is precies de koppeling waar #631 over gaat.

Nu S3Panel, met een expliciete API: het formulier dat het bewerkt, de weg naar
de certificaatbevestiging, en een melding terug wanneer er iets veranderde. Die
melding is niet decoratief — de statusregel achter de verbindingsnaam toont de
uitslag van de verbindingstest en staat buiten het paneel.

S3Form en KeychainSecret verhuisden mee naar lib/widgets/dialogs/settings/,
omdat een part-klasse niet van buiten de bibliotheek te gebruiken is.

Het paneel is daarmee te toetsen zonder de dialoog te openen, en die test staat
er: renderen, typen, en dat een gewijzigde adresseringsstijl de vorige
testuitslag laat vervallen. Zonder dat laatste meldt het paneel "verbinding
gelukt" over een verbinding die het niet geprobeerd heeft.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Zelfde verhuizing als het S3-paneel, met WebdavForm mee. Daarmee zijn de twee
panelen die om een wachtwoord vragen uit de gedeelde part-scope, en is
`_webdavField` — de veldhelper die per ongeluk de naam van één bron droeg maar
door alle vier gebruikt werd — vervangen door SettingsTextField op de
overgebleven aanroepen in het git- en AI-paneel.

De typedef voor de certificaatbevestiging staat apart, niet in een van de twee
panelen: het ophalen van een certificaat leunt op dart:io en dat hoort een
paneel niet mee te slepen naar de webbundel.

De wachter in settings_search_test die controleert dat elke geïndexeerde
sectiekop nog getekend wordt, zocht letterlijk naar `_sectionTitle(`. Die kende
de widgetvorm niet en zou bij de volgende verhuizing stil groen blijven op een
kop die niemand meer rendert; hij kent nu beide vormen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Derde en laatste van de drie netwerkbronnen die om een geheim vragen. Zelfde
vorm als S3 en WebDAV, met GitForm mee uit de part-scope; een
ConsumerStatefulWidget omdat dit paneel als enige de detectie van native git
leest.

Daarmee is de vierde van de vier inloggegevensformulieren uit #631 nog het
enige dat in de gedeelde scope zit — de AI-backend, die geen certificaat en
geen verbindingstest heeft en dus een andere vorm krijgt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
De vierde en laatste van de vier inloggegevensformulieren uit #631. Het AI-
tabblad zelf blijft voorlopig een extensie: het is geen paneel in de
verbindingenlijst maar een tabblad dat zijn velden bij het openen initialiseert
en bij Opslaan wegschrijft, en dat is een andere ingreep dan deze drie.

Wat nu al klopt: de klasse die de API-sleutel vasthoudt staat niet meer in een
scope waar zesentwintig andere onderwerpen bij kunnen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SOURCE_MAP beschreef `parts/settings_dialog_webdav.dart` en zijn buren; die
bestaan niet meer. De nieuwe indeling staat er nu, per bestand, mét de reden
dat `lib/widgets/dialogs/settings/` bestaat: alles onder `parts/` deelt één
privé-scope met _SettingsDialogState, en geen poort ziet dat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Twaalf hints — voorbeeld-URL's, een regio, een branchnaam, een submap — stonden
als kale literal in de invulvelden van WebDAV, S3, git en de AI-backend. Ze
kwamen wél op het scherm.

De poort zag ze niet. `_webdavField` was een `extension`-methode, en daar kijkt
de datastroomanalyse van check_hardcoded_text niet doorheen; zodra het een
gewone widget werd, vielen alle twaalf om. De overtreding is dus niet nieuw —
alleen zichtbaar geworden, en dat is precies wat de verhuizing van #631 zou
moeten opleveren.

Tien zijn identifiers of neutrale voorbeelden (`main`, `decks`, `eu-central-1`,
een loopback-URL) en staan nu in unchangedInAllLanguages: door d() heen, maar
zonder er een nepvertaling van te maken.

Twee waren echt Nederlands (`presentaties`, `/Presentaties`) en zijn in alle 31
talen vertaald.

En twee waren Nederlandse voorbeelddomeinen (`cloud.voorbeeld.nl`,
`dav.voorbeeld.nl`). Die zijn `example.com` geworden in plaats van 62 keer
vertaald: een voorbeeldhostnaam die in elke taal hetzelfde is, is beter dan
eenendertig varianten van een adres dat niet bestaat — en het sluit aan op het
git-veld, dat al `git.example.org` toonde.

Het typedef-bestand `confirm_certificate.dart` staat met reden in
uncoveredBaseline: één typedef, geen uitvoerbare regels, dus lcov schrijft er
geen record voor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brenno merged commit 78a84d481b into main 2026-07-23 09:29:34 +00:00
Sign in to join this conversation.
No description provided.