diff --git a/MACHBARKEITSSTUDIE.md b/MACHBARKEITSSTUDIE.md index 9305180..c1603d0 100644 --- a/MACHBARKEITSSTUDIE.md +++ b/MACHBARKEITSSTUDIE.md @@ -1,4 +1,4 @@ -# Recl∆Im (F∆I) — Machbarkeitsstudie & Umsetzungsplan +# F∆I Law-Heatmap — Machbarkeitsstudie & Umsetzungsplan > Arbeitstitel des Systems: **„Gesetzes-Heatmap" / „Regulierungs-Wirkungsanalyse auf Ch∆In-Basis"** > Auftraggeber-Kontext: MIT AG Digitalisierung & KI-Strategien (Vorsitz Dr. S. Flemming), diff --git a/METHODIK.md b/METHODIK.md index bb3d292..6afb9a9 100644 --- a/METHODIK.md +++ b/METHODIK.md @@ -1,6 +1,6 @@ # Methodik-Papier — Pflicht-basierte Wirkungsanalyse von Rechtsnormen -> Standalone-Auszug aus der Machbarkeitsstudie Recl∆Im (F∆I), +> Standalone-Auszug aus der Machbarkeitsstudie F∆I Law-Heatmap, > destilliert für AG-Vorlage und Beirats-Konsultation. > > Version v0.1.0 · 2026-06 · Diskussionsgrundlage, AG-intern. diff --git a/RUN.md b/RUN.md index 05501ba..d4c6c08 100644 --- a/RUN.md +++ b/RUN.md @@ -1,4 +1,4 @@ -# Recl∆Im (F∆I) — Run-Guide (Phase 0) +# F∆I Law-Heatmap — Run-Guide (Phase 0) > Stand v0.1.0 — Demo-Modus, ohne externe Daten, ohne Hub-Verbindung. @@ -39,7 +39,7 @@ eigene Crate-Verzeichnisse, ABI `chain:platform`, alle Apache 2.0: cd app ./setup.sh # Fonts + flutter pub get ./build-macos.sh # Robust-Build (strippt macOS-xattrs vor CodeSign) -open build/macos/Build/Products/Debug/reclaim_app.app +open build/macos/Build/Products/Debug/lawheatmap_app.app ``` Direkter `flutter run -d macos` funktioniert auch, kann aber bei diff --git a/app/README.md b/app/README.md index e0c8e2e..241dc2b 100644 --- a/app/README.md +++ b/app/README.md @@ -1,6 +1,6 @@ -# reclaim_app +# lawheatmap_app -Recl∆Im (F∆I) client +F∆I Law-Heatmap client ## Getting Started diff --git a/app/build-macos.sh b/app/build-macos.sh index c4ff804..87938f6 100755 --- a/app/build-macos.sh +++ b/app/build-macos.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Robust macOS build for the Recl∆Im (F∆I) client. +# Robust macOS build for the F∆I Law-Heatmap client. # # macOS xattrs (especially `com.apple.provenance` on files that # touched iCloud or were downloaded by curl) make Xcode's @@ -30,7 +30,7 @@ flutter build macos --debug \ echo " flutter exited $rc — continuing to xattr+resign step in case the binary linked" } -APP=build/macos/Build/Products/Debug/reclaim_app.app +APP=build/macos/Build/Products/Debug/lawheatmap_app.app if [ ! -d "$APP" ]; then echo "ERROR: $APP does not exist — flutter build failed before linking" exit 1 @@ -45,4 +45,4 @@ echo "[4/4] ad-hoc resign" echo echo "done. Launch with:" echo " open $APP" -echo " # or directly: $APP/Contents/MacOS/reclaim_app" +echo " # or directly: $APP/Contents/MacOS/lawheatmap_app" diff --git a/app/lib/data/fixtures.dart b/app/lib/data/fixtures.dart index 18980ee..57fcd25 100644 --- a/app/lib/data/fixtures.dart +++ b/app/lib/data/fixtures.dart @@ -29,26 +29,6 @@ class Fixtures { standDate: DateTime.utc(2025, 12, 1), sourceUrl: 'https://www.gesetze-im-internet.de/gewo/__14.html', sourceSha256: 'demo-${'a' * 60}', - paragraphs: const [ - NormParagraph( - number: '(1)', - text: 'Wer den selbständigen Betrieb eines stehenden Gewerbes, ' - 'einer Zweigniederlassung oder einer unselbständigen ' - 'Zweigstelle anfängt, muss dies der zuständigen Behörde ' - 'gleichzeitig anzeigen. Das Gleiche gilt, wenn ' - '1. der Betrieb verlegt wird, ' - '2. der Gegenstand des Gewerbes gewechselt oder ' - 'auf Waren oder Leistungen ausgedehnt wird, die bei ' - 'Gewerbebetrieben der angemeldeten Art nicht geschäftsüblich ' - 'sind, oder ' - '3. der Betrieb aufgegeben wird.', - ), - NormParagraph( - number: '(2)', - text: 'Die Anzeige hat unverzüglich zu erfolgen. Sie ist nach ' - 'amtlich vorgeschriebenem Vordruck zu erstatten.', - ), - ], ), skmEurPerYear: 2_800_000, benefitScore: 2.5, @@ -98,30 +78,6 @@ class Fixtures { sourceUrl: 'https://www.gesetze-im-internet.de/kassensichv/index.html', sourceSha256: 'demo-${'b' * 60}', - paragraphs: const [ - NormParagraph( - number: '§ 1', - text: 'Diese Verordnung gilt für elektronische ' - 'Aufzeichnungssysteme im Sinne des § 146a Absatz 1 ' - 'Satz 1 der Abgabenordnung, mit denen aufzeichnungspflichtige ' - 'Geschäftsvorfälle oder andere Vorgänge erfasst werden.', - ), - NormParagraph( - number: '§ 2', - text: 'Das elektronische Aufzeichnungssystem muss jeden ' - 'einzelnen aufzeichnungspflichtigen Vorgang vollständig ' - 'aufzeichnen. Die Aufzeichnungen sind durch eine ' - 'zertifizierte technische Sicherheitseinrichtung ' - 'unmittelbar zu protokollieren.', - ), - NormParagraph( - number: '§ 3', - text: 'Den Steuerpflichtigen treffen Mitteilungspflichten ' - 'gegenüber dem zuständigen Finanzamt; der Ausfall einer ' - 'zertifizierten technischen Sicherheitseinrichtung ist ' - 'unverzüglich anzuzeigen.', - ), - ], ), skmEurPerYear: 25_000_000, benefitScore: 3.0, @@ -180,48 +136,6 @@ class Fixtures { standDate: DateTime.utc(2025, 10, 1), sourceUrl: 'https://eur-lex.europa.eu/eli/reg/2016/679', sourceSha256: 'demo-${'c' * 60}', - paragraphs: const [ - NormParagraph( - number: '(1)', - text: 'Jeder Verantwortliche und gegebenenfalls sein Vertreter ' - 'führen ein Verzeichnis aller Verarbeitungstätigkeiten, ' - 'die ihrer Zuständigkeit unterliegen. Dieses Verzeichnis ' - 'enthält sämtliche folgenden Angaben: a) den Namen und ' - 'die Kontaktdaten des Verantwortlichen … b) die Zwecke ' - 'der Verarbeitung; c) eine Beschreibung der Kategorien ' - 'betroffener Personen und der Kategorien personenbezogener ' - 'Daten; d) die Kategorien von Empfängern …', - ), - NormParagraph( - number: '(2)', - text: 'Jeder Auftragsverarbeiter und gegebenenfalls sein ' - 'Vertreter führen ein Verzeichnis zu allen Kategorien von ' - 'im Auftrag eines Verantwortlichen durchgeführten ' - 'Verarbeitungstätigkeiten.', - ), - NormParagraph( - number: '(3)', - text: 'Das in den Absätzen 1 und 2 genannte Verzeichnis ist ' - 'schriftlich zu führen, was auch in einem elektronischen ' - 'Format erfolgen kann.', - ), - NormParagraph( - number: '(4)', - text: 'Der Verantwortliche oder der Auftragsverarbeiter sowie ' - 'gegebenenfalls deren Vertreter stellen das Verzeichnis ' - 'der Aufsichtsbehörde auf Anfrage zur Verfügung.', - ), - NormParagraph( - number: '(5)', - text: 'Die in den Absätzen 1 und 2 genannten Pflichten gelten ' - 'nicht für Unternehmen oder Einrichtungen, die weniger als ' - '250 Mitarbeiter beschäftigen, es sei denn die von ihnen ' - 'vorgenommene Verarbeitung birgt ein Risiko für die Rechte ' - 'und Freiheiten der betroffenen Personen, die Verarbeitung ' - 'erfolgt nicht nur gelegentlich oder umfasst besondere ' - 'Datenkategorien gemäß Artikel 9 Absatz 1 …', - ), - ], ), skmEurPerYear: 450_000_000, benefitScore: 3.8, @@ -271,31 +185,6 @@ class Fixtures { sourceUrl: 'https://gesetze.berlin.de/bsbe/document/jlr-BauOBE2005V25P60', sourceSha256: 'demo-${'d' * 60}', - paragraphs: const [ - NormParagraph( - number: '§ 60 (1)', - text: 'Für die Errichtung, Änderung und Nutzungsänderung ' - 'baulicher Anlagen sowie anderer Anlagen und ' - 'Einrichtungen im Sinne des § 1 Absatz 1 Satz 2 ist die ' - 'Baugenehmigung erforderlich, soweit in den §§ 61 bis 65 ' - 'sowie 76 und 77 nichts anderes bestimmt ist.', - ), - NormParagraph( - number: '§ 60 (2)', - text: 'Bauliche Anlagen und andere Anlagen, an die in dieser ' - 'Bauordnung oder in Vorschriften aufgrund dieser ' - 'Bauordnung Anforderungen gestellt werden, dürfen nur so ' - 'errichtet, geändert und in der Nutzung geändert werden, ' - 'dass sie diesen Anforderungen entsprechen.', - ), - NormParagraph( - number: '§ 63', - text: 'Der Bauantrag ist schriftlich bei der Bauaufsichtsbehörde ' - 'unter Beifügung der für die Beurteilung des Vorhabens und ' - 'die Bearbeitung des Bauantrags erforderlichen Bauvorlagen ' - 'einzureichen.', - ), - ], ), skmEurPerYear: 72_000_000, benefitScore: 3.5, @@ -344,30 +233,6 @@ class Fixtures { standDate: DateTime.utc(2025, 11, 1), sourceUrl: 'https://www.gesetze-im-internet.de/milog/__17.html', sourceSha256: 'demo-${'e' * 60}', - paragraphs: const [ - NormParagraph( - number: '(1)', - text: 'Ein Arbeitgeber, der Arbeitnehmerinnen und Arbeitnehmer ' - 'nach § 8 Absatz 1 des Vierten Buches Sozialgesetzbuch ' - 'geringfügig beschäftigt oder in den in § 2a des ' - 'Schwarzarbeitsbekämpfungsgesetzes genannten ' - 'Wirtschaftsbereichen oder Wirtschaftszweigen Personen ' - 'beschäftigt, ist verpflichtet, Beginn, Ende und Dauer der ' - 'täglichen Arbeitszeit dieser Arbeitnehmerinnen und ' - 'Arbeitnehmer spätestens bis zum Ablauf des siebten auf ' - 'den Tag der Arbeitsleistung folgenden Kalendertages ' - 'aufzuzeichnen und diese Aufzeichnungen mindestens zwei ' - 'Jahre beginnend ab dem für die Aufzeichnung maßgeblichen ' - 'Zeitpunkt aufzubewahren.', - ), - NormParagraph( - number: '(2)', - text: 'Arbeitgeber im Sinne des Absatzes 1 haben die für die ' - 'Kontrolle der Einhaltung der Verpflichtungen nach § 20 ' - 'in Verbindung mit § 2 Absatz 1 erforderlichen Unterlagen ' - 'im Inland in deutscher Sprache bereitzuhalten.', - ), - ], ), skmEurPerYear: 270_000_000, benefitScore: 3.2, diff --git a/app/lib/data/models.dart b/app/lib/data/models.dart index 309835f..d439635 100644 --- a/app/lib/data/models.dart +++ b/app/lib/data/models.dart @@ -1,4 +1,4 @@ -// Domain model for the F∆I Recl∆Im client. +// Domain model for the F∆I Law-Heatmap client. // // These types are the lingua franca between the (eventually // gRPC-fed) repository and the UI. Names mirror the Studie's @@ -75,16 +75,6 @@ class Duty { final String? consequence; } -/// One Absatz/paragraph of the actual normative text. -class NormParagraph { - const NormParagraph({required this.number, required this.text}); - - /// Source numbering as it appears, e.g. "(1)", "(2a)". Empty - /// when the source has no Absatz numbering. - final String number; - final String text; -} - /// One norm version — a versioned legal text identified by its /// (synthetic for now) ELI/CELEX. class Norm { @@ -96,7 +86,6 @@ class Norm { required this.standDate, required this.sourceUrl, required this.sourceSha256, - this.paragraphs = const [], }); final String eli; @@ -106,12 +95,6 @@ class Norm { final DateTime standDate; final String sourceUrl; final String sourceSha256; - - /// The actual paragraphs of the norm text, normalised by - /// `text.akoma_normalize` in live mode. In demo mode the - /// fixture quotes the public source verbatim under §5 UrhG - /// (amtliche Werke). - final List paragraphs; } /// A heatmap point: one norm with its computed scores. diff --git a/app/lib/main.dart b/app/lib/main.dart index 733c55c..82db2aa 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -5,13 +5,13 @@ import 'data/hub_endpoint.dart'; import 'data/hub_repository.dart'; import 'data/repository.dart'; import 'pages/landing_page.dart'; -import 'theme/reclaim_theme.dart'; +import 'theme/lawheatmap_theme.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); final settings = await HubSettings.load(); final repository = await _pickRepository(settings); - runApp(ReclaimApp(repository: repository, settings: settings)); + runApp(LawHeatmapApp(repository: repository, settings: settings)); } /// Decide which repository to start with. @@ -28,8 +28,8 @@ Future _pickRepository(HubSettings settings) async { return const MockRepository(); } -class ReclaimApp extends StatelessWidget { - const ReclaimApp({ +class LawHeatmapApp extends StatelessWidget { + const LawHeatmapApp({ super.key, this.repository = const MockRepository(), this.settings, @@ -41,10 +41,10 @@ class ReclaimApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Recl∆Im', + title: 'F∆I Law-Heatmap', debugShowCheckedModeBanner: false, - theme: ReclaimTheme.light(), - darkTheme: ReclaimTheme.dark(), + theme: LawHeatmapTheme.light(), + darkTheme: LawHeatmapTheme.dark(), themeMode: ThemeMode.dark, localizationsDelegates: const [ GlobalMaterialLocalizations.delegate, diff --git a/app/lib/pages/about_page.dart b/app/lib/pages/about_page.dart index cff1356..e7434f7 100644 --- a/app/lib/pages/about_page.dart +++ b/app/lib/pages/about_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import '../theme/reclaim_tokens.dart'; -import '../widgets/reclaim_card.dart'; +import '../theme/lawheatmap_tokens.dart'; +import '../widgets/lawheatmap_card.dart'; class AboutPage extends StatelessWidget { const AboutPage({super.key}); @@ -10,25 +10,25 @@ class AboutPage extends StatelessWidget { Widget build(BuildContext context) { final t = Theme.of(context).textTheme; return SingleChildScrollView( - padding: const EdgeInsets.all(ReclaimSpace.xl), + padding: const EdgeInsets.all(LawHeatmapSpace.xl), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Methodik & Quellen', style: t.displaySmall), - const SizedBox(height: ReclaimSpace.xs), + const SizedBox(height: LawHeatmapSpace.xs), Text( 'Wie das System bewertet, woran es sich messen lässt.', - style: t.bodyLarge?.copyWith(color: ReclaimColors.mute), + style: t.bodyLarge?.copyWith(color: LawHeatmapColors.mute), ), - const SizedBox(height: ReclaimSpace.lg), - ReclaimCard( + const SizedBox(height: LawHeatmapSpace.lg), + LawHeatmapCard( accent: true, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Drei nicht verhandelbare Sätze', style: t.headlineSmall), - const SizedBox(height: ReclaimSpace.md), + const SizedBox(height: LawHeatmapSpace.md), Text( '1. Wir bewerten Pflichten, nicht Gesetze.\n' '2. Jede Zahl trägt die Tier-Stufe ihres schwächsten ' @@ -40,13 +40,13 @@ class AboutPage extends StatelessWidget { ], ), ), - const SizedBox(height: ReclaimSpace.lg), - ReclaimCard( + const SizedBox(height: LawHeatmapSpace.lg), + LawHeatmapCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Methodische Vorbilder', style: t.headlineSmall), - const SizedBox(height: ReclaimSpace.sm), + const SizedBox(height: LawHeatmapSpace.sm), _bullet(t, 'Standardkostenmodell (SKM) — Nationaler Normenkontrollrat. P × F × T × h.'), _bullet(t, @@ -62,13 +62,13 @@ class AboutPage extends StatelessWidget { ], ), ), - const SizedBox(height: ReclaimSpace.lg), - ReclaimCard( + const SizedBox(height: LawHeatmapSpace.lg), + LawHeatmapCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('4-Tier-Evidenz-Modell', style: t.headlineSmall), - const SizedBox(height: ReclaimSpace.sm), + const SizedBox(height: LawHeatmapSpace.sm), _bullet(t, 'T1 — amtlich/peer-reviewed: NKR, DESTATIS, ifo, ZEW, BMWK, SVR, IfM Bonn.'), _bullet(t, @@ -80,13 +80,13 @@ class AboutPage extends StatelessWidget { ], ), ), - const SizedBox(height: ReclaimSpace.lg), - ReclaimCard( + const SizedBox(height: LawHeatmapSpace.lg), + LawHeatmapCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Plattform', style: t.headlineSmall), - const SizedBox(height: ReclaimSpace.sm), + const SizedBox(height: LawHeatmapSpace.sm), Text( 'Ch∆In v0.16.0 — Vendor: F∆I = Flemming.AI. ' 'Hub. Module. Flow. Deterministisch, WASM-sandboxed, ' @@ -104,7 +104,7 @@ class AboutPage extends StatelessWidget { Widget _bullet(TextTheme t, String text) { return Padding( - padding: const EdgeInsets.only(bottom: ReclaimSpace.sm), + padding: const EdgeInsets.only(bottom: LawHeatmapSpace.sm), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -114,7 +114,7 @@ class AboutPage extends StatelessWidget { width: 5, height: 5, decoration: const BoxDecoration( - color: ReclaimColors.signal, + color: LawHeatmapColors.signal, shape: BoxShape.circle, ), ), diff --git a/app/lib/pages/heatmap_page.dart b/app/lib/pages/heatmap_page.dart index 9fd69ce..37dc7f1 100644 --- a/app/lib/pages/heatmap_page.dart +++ b/app/lib/pages/heatmap_page.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import '../data/models.dart'; import '../data/repository.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_tokens.dart'; import '../widgets/demo_banner.dart'; import '../widgets/heatmap_grid.dart'; -import '../widgets/reclaim_card.dart'; +import '../widgets/lawheatmap_card.dart'; import '../widgets/tier_badge.dart'; import 'norm_detail_page.dart'; @@ -35,7 +35,7 @@ class _HeatmapPageState extends State { const DemoBanner(), Expanded( child: Padding( - padding: const EdgeInsets.all(ReclaimSpace.xl), + padding: const EdgeInsets.all(LawHeatmapSpace.xl), child: FutureBuilder>( future: _future, builder: (context, snap) { @@ -48,18 +48,18 @@ class _HeatmapPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Heatmap', style: t.displaySmall), - const SizedBox(height: ReclaimSpace.xs), + const SizedBox(height: LawHeatmapSpace.xs), Text( 'X: jährliche Bürokratiekosten (€, log10). ' 'Y: Nutzen-Score 0–5. Punktgröße: Betroffenheit. ' 'Klick auf einen Punkt → Detail mit Pflichten + Quellen.', style: t.bodyLarge?.copyWith( - color: ReclaimColors.mute, + color: LawHeatmapColors.mute, ), ), - const SizedBox(height: ReclaimSpace.lg), + const SizedBox(height: LawHeatmapSpace.lg), Expanded( - child: ReclaimCard( + child: LawHeatmapCard( accent: true, expand: true, child: HeatmapGrid( @@ -77,10 +77,10 @@ class _HeatmapPageState extends State { ), ), ), - const SizedBox(height: ReclaimSpace.lg), + const SizedBox(height: LawHeatmapSpace.lg), Wrap( - spacing: ReclaimSpace.md, - runSpacing: ReclaimSpace.sm, + spacing: LawHeatmapSpace.md, + runSpacing: LawHeatmapSpace.sm, children: [ for (final e in items) InkWell( @@ -94,10 +94,10 @@ class _HeatmapPageState extends State { ), ); }, - child: ReclaimCard( + child: LawHeatmapCard( padding: const EdgeInsets.symmetric( - horizontal: ReclaimSpace.md, - vertical: ReclaimSpace.sm, + horizontal: LawHeatmapSpace.md, + vertical: LawHeatmapSpace.sm, ), child: Row( mainAxisSize: MainAxisSize.min, @@ -105,7 +105,7 @@ class _HeatmapPageState extends State { TierBadge( tier: e.tierLowest, compact: true), const SizedBox( - width: ReclaimSpace.sm), + width: LawHeatmapSpace.sm), Text( '${e.norm.jurabk} ${e.norm.paragraph}', style: t.labelLarge, diff --git a/app/lib/pages/hub_status_page.dart b/app/lib/pages/hub_status_page.dart index 906677f..8784ecb 100644 --- a/app/lib/pages/hub_status_page.dart +++ b/app/lib/pages/hub_status_page.dart @@ -3,9 +3,9 @@ import 'package:flutter/material.dart'; import '../data/hub_endpoint.dart'; import '../data/hub_repository.dart'; import '../data/repository.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_tokens.dart'; import '../widgets/hub_capabilities_card.dart'; -import '../widgets/reclaim_card.dart'; +import '../widgets/lawheatmap_card.dart'; /// Hub connection + settings. Read-only summary at the top, then /// an editable form for endpoint host/port/secure/token, then a @@ -109,18 +109,18 @@ class _HubStatusPageState extends State { final repo = widget.repository; final s = _settings; return SingleChildScrollView( - padding: const EdgeInsets.all(ReclaimSpace.xl), + padding: const EdgeInsets.all(LawHeatmapSpace.xl), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Ch∆In Hub', style: t.displaySmall), - const SizedBox(height: ReclaimSpace.xs), + const SizedBox(height: LawHeatmapSpace.xs), Text( 'Status der Verbindung und Konfiguration des lokalen Hubs.', - style: t.bodyLarge?.copyWith(color: ReclaimColors.mute), + style: t.bodyLarge?.copyWith(color: LawHeatmapColors.mute), ), - const SizedBox(height: ReclaimSpace.lg), - ReclaimCard( + const SizedBox(height: LawHeatmapSpace.lg), + LawHeatmapCard( accent: true, child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -142,23 +142,23 @@ class _HubStatusPageState extends State { ], ), ), - const SizedBox(height: ReclaimSpace.lg), - ReclaimCard( + const SizedBox(height: LawHeatmapSpace.lg), + LawHeatmapCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Verbindung konfigurieren', style: t.headlineSmall), - const SizedBox(height: ReclaimSpace.sm), + const SizedBox(height: LawHeatmapSpace.sm), Text( '`chain serve` läuft standardmäßig auf ' '127.0.0.1:50051 ohne Auth. ' 'Bei aktiver Auth den Bearer-Token aus ' '~/.chain/hub-auth-token hier eintragen.', style: t.labelSmall - ?.copyWith(color: ReclaimColors.mute), + ?.copyWith(color: LawHeatmapColors.mute), ), - const SizedBox(height: ReclaimSpace.lg), + const SizedBox(height: LawHeatmapSpace.lg), Row(children: [ Expanded( flex: 3, @@ -170,7 +170,7 @@ class _HubStatusPageState extends State { ), ), ), - const SizedBox(width: ReclaimSpace.md), + const SizedBox(width: LawHeatmapSpace.md), Expanded( child: TextField( controller: _portCtl, @@ -182,7 +182,7 @@ class _HubStatusPageState extends State { ), ), ]), - const SizedBox(height: ReclaimSpace.md), + const SizedBox(height: LawHeatmapSpace.md), TextField( controller: _tokenCtl, decoration: const InputDecoration( @@ -190,23 +190,23 @@ class _HubStatusPageState extends State { ), obscureText: true, ), - const SizedBox(height: ReclaimSpace.md), + const SizedBox(height: LawHeatmapSpace.md), Row(children: [ Switch( value: _secure, onChanged: (v) => setState(() => _secure = v), ), - const SizedBox(width: ReclaimSpace.sm), + const SizedBox(width: LawHeatmapSpace.sm), const Text('TLS (https)'), - const SizedBox(width: ReclaimSpace.xl), + const SizedBox(width: LawHeatmapSpace.xl), Switch( value: _useHub, onChanged: (v) => setState(() => _useHub = v), ), - const SizedBox(width: ReclaimSpace.sm), + const SizedBox(width: LawHeatmapSpace.sm), const Text('Hub beim Start nutzen'), ]), - const SizedBox(height: ReclaimSpace.lg), + const SizedBox(height: LawHeatmapSpace.lg), Row(children: [ FilledButton.icon( onPressed: _probing ? null : _probe, @@ -221,7 +221,7 @@ class _HubStatusPageState extends State { : const Icon(Icons.bolt_outlined), label: const Text('Verbindung testen'), ), - const SizedBox(width: ReclaimSpace.md), + const SizedBox(width: LawHeatmapSpace.md), OutlinedButton.icon( onPressed: _save, icon: const Icon(Icons.save_outlined), @@ -229,24 +229,24 @@ class _HubStatusPageState extends State { ), ]), if (_probeMessage != null) ...[ - const SizedBox(height: ReclaimSpace.md), + const SizedBox(height: LawHeatmapSpace.md), Text(_probeMessage!, style: t.labelLarge?.copyWith( - color: ReclaimColors.signal, + color: LawHeatmapColors.signal, )), ], ], ), ), - const SizedBox(height: ReclaimSpace.lg), + const SizedBox(height: LawHeatmapSpace.lg), HubCapabilitiesCard(repository: repo), - const SizedBox(height: ReclaimSpace.lg), - ReclaimCard( + const SizedBox(height: LawHeatmapSpace.lg), + LawHeatmapCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Geplanter Live-Flow', style: t.headlineSmall), - const SizedBox(height: ReclaimSpace.sm), + const SizedBox(height: LawHeatmapSpace.sm), Text( '1. `chain serve` auf localhost:50051 starten.\n' '2. Module installieren (`chain install`).\n' @@ -261,7 +261,7 @@ class _HubStatusPageState extends State { 'Evaluation-Objekte übersetzt.', style: t.bodyLarge?.copyWith( height: 1.6, - fontFamily: ReclaimTypography.mono, + fontFamily: LawHeatmapTypography.mono, fontSize: 12, ), ), @@ -282,12 +282,12 @@ class _HubStatusPageState extends State { width: 200, child: Text(label, style: t.labelLarge - ?.copyWith(color: ReclaimColors.mute)), + ?.copyWith(color: LawHeatmapColors.mute)), ), Expanded( child: Text(value, style: t.bodyLarge - ?.copyWith(fontFamily: ReclaimTypography.mono)), + ?.copyWith(fontFamily: LawHeatmapTypography.mono)), ), ], ), diff --git a/app/lib/pages/landing_page.dart b/app/lib/pages/landing_page.dart index 0f19bac..a10745d 100644 --- a/app/lib/pages/landing_page.dart +++ b/app/lib/pages/landing_page.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import '../data/repository.dart'; -import '../theme/reclaim_theme.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_theme.dart'; +import '../theme/lawheatmap_tokens.dart'; import '../widgets/delta_mark.dart'; import 'shell_page.dart'; @@ -23,28 +23,28 @@ class LandingPage extends StatelessWidget { constraints: const BoxConstraints(maxWidth: 720), child: Padding( padding: const EdgeInsets.symmetric( - horizontal: ReclaimSpace.xxl, - vertical: ReclaimSpace.xxxl, + horizontal: LawHeatmapSpace.xxl, + vertical: LawHeatmapSpace.xxxl, ), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ const DeltaMark(size: 96), - const SizedBox(height: ReclaimSpace.xxl), - Text('Recl∆Im', style: t.displaySmall), - const SizedBox(height: ReclaimSpace.sm), + const SizedBox(height: LawHeatmapSpace.xxl), + Text('F∆I Law-Heatmap', style: t.displaySmall), + const SizedBox(height: LawHeatmapSpace.sm), Text( 'Pflicht-basierte Wirkungsanalyse von Rechtsnormen — ' 'auf Ch∆In, mit Quellen-Audit by construction.', style: t.bodyLarge - ?.copyWith(color: ReclaimColors.mute), + ?.copyWith(color: LawHeatmapColors.mute), ), - const SizedBox(height: ReclaimSpace.xxl), + const SizedBox(height: LawHeatmapSpace.xxl), Wrap( crossAxisAlignment: WrapCrossAlignment.center, - spacing: ReclaimSpace.md, - runSpacing: ReclaimSpace.sm, + spacing: LawHeatmapSpace.md, + runSpacing: LawHeatmapSpace.sm, children: [ FilledButton.icon( onPressed: () { @@ -61,18 +61,18 @@ class LandingPage extends StatelessWidget { Text( 'Demo: 5 Pilot-Normen, ohne Hub-Verbindung', style: t.labelSmall?.copyWith( - color: ReclaimColors.mute, + color: LawHeatmapColors.mute, ), ), ], ), - const SizedBox(height: ReclaimSpace.xl), + const SizedBox(height: LawHeatmapSpace.xl), Text( 'v0.1.0 · Phase 0 · Mock-Modus · ' 'Live-Hub-Anbindung folgt in Woche 1', - style: ReclaimTheme.mono( + style: LawHeatmapTheme.mono( t.labelSmall ?? const TextStyle(), - ).copyWith(color: ReclaimColors.mute), + ).copyWith(color: LawHeatmapColors.mute), ), ], ), diff --git a/app/lib/pages/norm_detail_page.dart b/app/lib/pages/norm_detail_page.dart index 0185ab5..e94accb 100644 --- a/app/lib/pages/norm_detail_page.dart +++ b/app/lib/pages/norm_detail_page.dart @@ -2,11 +2,10 @@ import 'package:flutter/material.dart'; import '../data/models.dart'; import '../data/repository.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_tokens.dart'; import '../widgets/demo_banner.dart'; import '../widgets/evidence_sidebar.dart'; -import '../widgets/norm_text_card.dart'; -import '../widgets/reclaim_card.dart'; +import '../widgets/lawheatmap_card.dart'; import '../widgets/tier_badge.dart'; class NormDetailPage extends StatelessWidget { @@ -32,7 +31,7 @@ class NormDetailPage extends StatelessWidget { const DemoBanner(), Expanded( child: SingleChildScrollView( - padding: const EdgeInsets.all(ReclaimSpace.xl), + padding: const EdgeInsets.all(LawHeatmapSpace.xl), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -56,8 +55,8 @@ class NormDetailPage extends StatelessWidget { e.norm.eli, style: t.bodyLarge?.copyWith( fontFamily: - ReclaimTypography.mono, - color: ReclaimColors.mute, + LawHeatmapTypography.mono, + color: LawHeatmapColors.mute, ), ), ], @@ -66,36 +65,34 @@ class NormDetailPage extends StatelessWidget { TierBadge(tier: e.tierLowest), ], ), - const SizedBox(height: ReclaimSpace.lg), + const SizedBox(height: LawHeatmapSpace.lg), _MetricsRow(e: e), - const SizedBox(height: ReclaimSpace.lg), - NormTextCard(norm: e.norm), - const SizedBox(height: ReclaimSpace.lg), - ReclaimCard( + const SizedBox(height: LawHeatmapSpace.lg), + LawHeatmapCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Pflichten', style: t.headlineSmall), const SizedBox( - height: ReclaimSpace.sm), + height: LawHeatmapSpace.sm), Text( 'Atomare deontische Aussagen, extrahiert ' 'aus dem Norm-Text (Phase 1: regex + LLM-' 'Vorschlag, Juristen-Review obligatorisch).', style: t.labelSmall?.copyWith( - color: ReclaimColors.mute), + color: LawHeatmapColors.mute), ), const SizedBox( - height: ReclaimSpace.md), + height: LawHeatmapSpace.md), for (final d in e.duties) _DutyTile(duty: d), ], ), ), if (e.notes != null) ...[ - const SizedBox(height: ReclaimSpace.lg), - ReclaimCard( + const SizedBox(height: LawHeatmapSpace.lg), + LawHeatmapCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -103,7 +100,7 @@ class NormDetailPage extends StatelessWidget { Text('Methodik-Notiz', style: t.headlineSmall), const SizedBox( - height: ReclaimSpace.sm), + height: LawHeatmapSpace.sm), Text(e.notes!, style: t.bodyLarge), ], ), @@ -112,7 +109,7 @@ class NormDetailPage extends StatelessWidget { ], ), ), - const SizedBox(width: ReclaimSpace.xl), + const SizedBox(width: LawHeatmapSpace.xl), SizedBox( width: 340, child: EvidenceSidebar(evaluation: e), @@ -140,19 +137,19 @@ class _MetricsRow extends StatelessWidget { value: _eur(e.skmEurPerYear), sub: 'SKM P×F×T×h', ), - const SizedBox(width: ReclaimSpace.md), + const SizedBox(width: LawHeatmapSpace.md), _MetricBox( label: 'Nutzen', value: '${e.benefitScore.toStringAsFixed(1)} / 5', sub: '7-dim. Score', ), - const SizedBox(width: ReclaimSpace.md), + const SizedBox(width: LawHeatmapSpace.md), _MetricBox( label: 'Betroffene', value: _kmu(e.affectedCount), sub: 'DESTATIS', ), - const SizedBox(width: ReclaimSpace.md), + const SizedBox(width: LawHeatmapSpace.md), _MetricBox( label: 'Frust', value: '${e.frustScore.toStringAsFixed(1)} / 10', @@ -190,19 +187,19 @@ class _MetricBox extends StatelessWidget { Widget build(BuildContext context) { final t = Theme.of(context).textTheme; return Expanded( - child: ReclaimCard( + child: LawHeatmapCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: t.labelSmall - ?.copyWith(color: ReclaimColors.mute)), + ?.copyWith(color: LawHeatmapColors.mute)), const SizedBox(height: 4), Text(value, style: t.displaySmall), const SizedBox(height: 4), Text(sub, style: t.labelSmall - ?.copyWith(color: ReclaimColors.mute)), + ?.copyWith(color: LawHeatmapColors.mute)), ], ), ), @@ -218,16 +215,16 @@ class _DutyTile extends StatelessWidget { Widget build(BuildContext context) { final t = Theme.of(context).textTheme; return Padding( - padding: const EdgeInsets.only(bottom: ReclaimSpace.md), + padding: const EdgeInsets.only(bottom: LawHeatmapSpace.md), child: Container( - padding: const EdgeInsets.all(ReclaimSpace.md), + padding: const EdgeInsets.all(LawHeatmapSpace.md), decoration: BoxDecoration( border: Border.all( color: Theme.of(context) .colorScheme .onSurface .withValues(alpha: 0.12)), - borderRadius: const BorderRadius.all(ReclaimRadius.sm), + borderRadius: const BorderRadius.all(LawHeatmapRadius.sm), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -237,26 +234,26 @@ class _DutyTile extends StatelessWidget { padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2), decoration: BoxDecoration( - color: ReclaimColors.signal.withValues(alpha: 0.18), + color: LawHeatmapColors.signal.withValues(alpha: 0.18), borderRadius: - const BorderRadius.all(ReclaimRadius.sm), + const BorderRadius.all(LawHeatmapRadius.sm), ), child: Text( duty.modality, style: t.labelSmall?.copyWith( - color: ReclaimColors.signal, + color: LawHeatmapColors.signal, fontWeight: FontWeight.w600, ), ), ), - const SizedBox(width: ReclaimSpace.sm), + const SizedBox(width: LawHeatmapSpace.sm), Text(duty.sourceNorm, style: t.labelSmall?.copyWith( - fontFamily: ReclaimTypography.mono, - color: ReclaimColors.mute, + fontFamily: LawHeatmapTypography.mono, + color: LawHeatmapColors.mute, )), ]), - const SizedBox(height: ReclaimSpace.sm), + const SizedBox(height: LawHeatmapSpace.sm), Text(duty.action, style: t.bodyLarge), const SizedBox(height: 4), Text( @@ -264,7 +261,7 @@ class _DutyTile extends StatelessWidget { 'Frequenz: ${duty.frequency}' '${duty.authority != null ? " · Behörde: ${duty.authority}" : ""}' '${duty.consequence != null ? " · Sanktion: ${duty.consequence}" : ""}', - style: t.labelSmall?.copyWith(color: ReclaimColors.mute), + style: t.labelSmall?.copyWith(color: LawHeatmapColors.mute), ), ], ), diff --git a/app/lib/pages/norms_list_page.dart b/app/lib/pages/norms_list_page.dart index 189a1fb..9f0d9b2 100644 --- a/app/lib/pages/norms_list_page.dart +++ b/app/lib/pages/norms_list_page.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import '../data/models.dart'; import '../data/repository.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_tokens.dart'; import '../widgets/demo_banner.dart'; -import '../widgets/reclaim_card.dart'; +import '../widgets/lawheatmap_card.dart'; import '../widgets/tier_badge.dart'; import 'norm_detail_page.dart'; @@ -21,20 +21,20 @@ class NormsListPage extends StatelessWidget { const DemoBanner(), Expanded( child: SingleChildScrollView( - padding: const EdgeInsets.all(ReclaimSpace.xl), + padding: const EdgeInsets.all(LawHeatmapSpace.xl), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Normen-Pilotkorpus', style: t.displaySmall), - const SizedBox(height: ReclaimSpace.xs), + const SizedBox(height: LawHeatmapSpace.xs), Text( '5 Normen für den vertikalen Durchstich. ' 'Korpus erweiterbar in Phase 1.', style: t.bodyLarge?.copyWith( - color: ReclaimColors.mute, + color: LawHeatmapColors.mute, ), ), - const SizedBox(height: ReclaimSpace.lg), + const SizedBox(height: LawHeatmapSpace.lg), FutureBuilder>( future: repository.list(), builder: (context, snap) { @@ -47,7 +47,7 @@ class NormsListPage extends StatelessWidget { for (final e in snap.data!) Padding( padding: const EdgeInsets.only( - bottom: ReclaimSpace.md), + bottom: LawHeatmapSpace.md), child: _NormRow( evaluation: e, repository: repository, @@ -86,7 +86,7 @@ class _NormRow extends StatelessWidget { ), ); }, - child: ReclaimCard( + child: LawHeatmapCard( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -103,14 +103,14 @@ class _NormRow extends StatelessWidget { Text( evaluation.norm.eli, style: t.labelSmall?.copyWith( - fontFamily: ReclaimTypography.mono, - color: ReclaimColors.mute, + fontFamily: LawHeatmapTypography.mono, + color: LawHeatmapColors.mute, ), ), ], ), ), - const SizedBox(width: ReclaimSpace.lg), + const SizedBox(width: LawHeatmapSpace.lg), Expanded( child: _MetricColumn( label: 'Schaden / Jahr', @@ -130,7 +130,7 @@ class _NormRow extends StatelessWidget { value: _kmu(evaluation.affectedCount), ), ), - const SizedBox(width: ReclaimSpace.md), + const SizedBox(width: LawHeatmapSpace.md), TierBadge(tier: evaluation.tierLowest), ], ), @@ -166,7 +166,7 @@ class _MetricColumn extends StatelessWidget { children: [ Text( label, - style: t.labelSmall?.copyWith(color: ReclaimColors.mute), + style: t.labelSmall?.copyWith(color: LawHeatmapColors.mute), ), const SizedBox(height: 2), Text(value, style: t.titleMedium), diff --git a/app/lib/pages/shell_page.dart b/app/lib/pages/shell_page.dart index fcbebde..75dc952 100644 --- a/app/lib/pages/shell_page.dart +++ b/app/lib/pages/shell_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../data/repository.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_tokens.dart'; import '../widgets/delta_mark.dart'; import 'about_page.dart'; import 'heatmap_page.dart'; @@ -42,18 +42,18 @@ class _ShellPageState extends State { onDestinationSelected: (i) => setState(() => _index = i), leading: Padding( padding: const EdgeInsets.symmetric( - vertical: ReclaimSpace.lg, - horizontal: ReclaimSpace.md, + vertical: LawHeatmapSpace.lg, + horizontal: LawHeatmapSpace.md, ), child: Column( children: [ const DeltaMark(size: 36), - const SizedBox(height: ReclaimSpace.sm), + const SizedBox(height: LawHeatmapSpace.sm), Text( 'F∆I', style: t.labelLarge?.copyWith( letterSpacing: 2, - color: ReclaimColors.signal, + color: LawHeatmapColors.signal, ), ), ], diff --git a/app/lib/theme/reclaim_theme.dart b/app/lib/theme/lawheatmap_theme.dart similarity index 73% rename from app/lib/theme/reclaim_theme.dart rename to app/lib/theme/lawheatmap_theme.dart index 012a9b2..c894707 100644 --- a/app/lib/theme/reclaim_theme.dart +++ b/app/lib/theme/lawheatmap_theme.dart @@ -1,35 +1,35 @@ import 'package:flutter/material.dart'; -import 'reclaim_tokens.dart'; +import 'lawheatmap_tokens.dart'; -/// ThemeData assembly for the F∆I Recl∆Im client. +/// ThemeData assembly for the F∆I Law-Heatmap client. /// /// Dark mode is the default (consistent with fai_web's /// `color-scheme: dark`). Light mode falls back to the paper /// palette for printing/screenshot work. -class ReclaimTheme { - ReclaimTheme._(); +class LawHeatmapTheme { + LawHeatmapTheme._(); static ThemeData dark() => _build(Brightness.dark); static ThemeData light() => _build(Brightness.light); static ThemeData _build(Brightness brightness) { final isDark = brightness == Brightness.dark; - final canvas = isDark ? ReclaimColors.ink : ReclaimColors.paper; + final canvas = isDark ? LawHeatmapColors.ink : LawHeatmapColors.paper; final surface = - isDark ? ReclaimColors.inkRaised : ReclaimColors.paperRaised; + isDark ? LawHeatmapColors.inkRaised : LawHeatmapColors.paperRaised; final onSurface = isDark ? const Color(0xFFE9E7E2) : const Color(0xFF1A1B1E); final colorScheme = ColorScheme( brightness: brightness, - primary: ReclaimColors.signal, - onPrimary: ReclaimColors.ink, - secondary: ReclaimColors.mute, + primary: LawHeatmapColors.signal, + onPrimary: LawHeatmapColors.ink, + secondary: LawHeatmapColors.mute, onSecondary: onSurface, error: const Color(0xFFD8723A), - onError: ReclaimColors.ink, + onError: LawHeatmapColors.ink, surface: surface, onSurface: onSurface, ); @@ -42,33 +42,33 @@ class ReclaimTheme { colorScheme: colorScheme, scaffoldBackgroundColor: canvas, canvasColor: canvas, - fontFamily: ReclaimTypography.body, + fontFamily: LawHeatmapTypography.body, textTheme: textTheme, cardTheme: CardThemeData( color: surface, elevation: 0, margin: EdgeInsets.zero, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(ReclaimRadius.md), + borderRadius: BorderRadius.all(LawHeatmapRadius.md), side: BorderSide(color: Color(0x24FFFFFF)), ), ), dividerTheme: DividerThemeData( - color: onSurface.withValues(alpha: ReclaimColors.hairlineDarkOpacity), + color: onSurface.withValues(alpha: LawHeatmapColors.hairlineDarkOpacity), thickness: 1, space: 1, ), filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( - backgroundColor: ReclaimColors.signal, - foregroundColor: ReclaimColors.ink, + backgroundColor: LawHeatmapColors.signal, + foregroundColor: LawHeatmapColors.ink, shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(ReclaimRadius.sm), + borderRadius: BorderRadius.all(LawHeatmapRadius.sm), ), textStyle: textTheme.labelLarge, padding: const EdgeInsets.symmetric( - horizontal: ReclaimSpace.lg, - vertical: ReclaimSpace.md, + horizontal: LawHeatmapSpace.lg, + vertical: LawHeatmapSpace.md, ), ), ), @@ -76,8 +76,8 @@ class ReclaimTheme { } static TextTheme _textTheme(Color onSurface) { - const display = ReclaimTypography.display; - const body = ReclaimTypography.body; + const display = LawHeatmapTypography.display; + const body = LawHeatmapTypography.body; return TextTheme( displaySmall: TextStyle( fontFamily: display, @@ -123,5 +123,5 @@ class ReclaimTheme { /// Convenience: JetBrains-Mono variant of an existing style for /// technical content (ELI/CELEX, SHA-256, audit-event IDs). static TextStyle mono(TextStyle base) => - base.copyWith(fontFamily: ReclaimTypography.mono); + base.copyWith(fontFamily: LawHeatmapTypography.mono); } diff --git a/app/lib/theme/reclaim_tokens.dart b/app/lib/theme/lawheatmap_tokens.dart similarity index 88% rename from app/lib/theme/reclaim_tokens.dart rename to app/lib/theme/lawheatmap_tokens.dart index 654bde6..a473a3e 100644 --- a/app/lib/theme/reclaim_tokens.dart +++ b/app/lib/theme/lawheatmap_tokens.dart @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; -/// Visual tokens for the F∆I Recl∆Im client, sourced from the +/// Visual tokens for the F∆I Law-Heatmap client, sourced from the /// fai_web brand palette (see fai_web/src/styles/global.css:14-20). /// /// Unlike `fai_chain_studio`'s `ChainColors` (Tailwind sky-400 — /// dev-tool look), this palette matches the public-facing brand: /// ink/paper with a petrol "signal" accent that doubles as the eye /// of the `∆` mark. -class ReclaimColors { - ReclaimColors._(); +class LawHeatmapColors { + LawHeatmapColors._(); // Core palette (fai_web/src/styles/global.css) static const ink = Color(0xFF08090A); @@ -40,8 +40,8 @@ class ReclaimColors { /// Spacing scale — 4/8/12/16/24/32/48 multiples, matching /// `ChainSpace` from `fai_chain_studio/lib/theme/tokens.dart`. -class ReclaimSpace { - ReclaimSpace._(); +class LawHeatmapSpace { + LawHeatmapSpace._(); static const xs = 4.0; static const sm = 8.0; @@ -52,15 +52,15 @@ class ReclaimSpace { static const xxxl = 48.0; } -class ReclaimRadius { - ReclaimRadius._(); +class LawHeatmapRadius { + LawHeatmapRadius._(); static const sm = Radius.circular(6); static const md = Radius.circular(10); } -class ReclaimTypography { - ReclaimTypography._(); +class LawHeatmapTypography { + LawHeatmapTypography._(); static const display = 'SpaceGroteskVariable'; static const body = 'InterVariable'; diff --git a/app/lib/widgets/delta_mark.dart b/app/lib/widgets/delta_mark.dart index fd921cb..30db44d 100644 --- a/app/lib/widgets/delta_mark.dart +++ b/app/lib/widgets/delta_mark.dart @@ -2,7 +2,7 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_tokens.dart'; /// The F∆I / Ch∆In brand mark: an upward triangle (the `∆`) with /// a petrol "eye" at the centre. Geometry mirrors fai_web's @@ -89,7 +89,7 @@ class _DeltaPainter extends CustomPainter { // Eye — petrol fill, centred on the triangle's centroid. final eyeCentre = Offset(w / 2, inset + (h - 2 * inset) * 2 / 3); final eyeR = w * 0.06 * pulse; - final eye = Paint()..color = ReclaimColors.signal; + final eye = Paint()..color = LawHeatmapColors.signal; canvas.drawCircle(eyeCentre, eyeR, eye); } diff --git a/app/lib/widgets/demo_banner.dart b/app/lib/widgets/demo_banner.dart index 0d41fc0..b697bc4 100644 --- a/app/lib/widgets/demo_banner.dart +++ b/app/lib/widgets/demo_banner.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_tokens.dart'; /// Permanent banner shown above all data screens in mock mode. /// Carries the political-defensibility caveat: this is demo data, @@ -14,29 +14,29 @@ class DemoBanner extends StatelessWidget { return Container( width: double.infinity, padding: const EdgeInsets.symmetric( - horizontal: ReclaimSpace.lg, - vertical: ReclaimSpace.sm, + horizontal: LawHeatmapSpace.lg, + vertical: LawHeatmapSpace.sm, ), decoration: BoxDecoration( - color: ReclaimColors.tierT4.withValues(alpha: 0.12), + color: LawHeatmapColors.tierT4.withValues(alpha: 0.12), border: Border( bottom: BorderSide( - color: ReclaimColors.tierT4.withValues(alpha: 0.55), + color: LawHeatmapColors.tierT4.withValues(alpha: 0.55), ), ), ), child: Row( children: [ const Icon(Icons.science_outlined, - size: 18, color: ReclaimColors.tierT4), - const SizedBox(width: ReclaimSpace.sm), + size: 18, color: LawHeatmapColors.tierT4), + const SizedBox(width: LawHeatmapSpace.sm), Expanded( child: Text( 'Demo-Daten — Phase 0, ohne Verbands-Erhebung. ' 'Keine Juristen-Approval, keine Beirats-Validierung. ' 'Alle Werte tragen Tier T4, bis T1/T2-Pipeline läuft.', style: t.labelSmall?.copyWith( - color: ReclaimColors.tierT4, + color: LawHeatmapColors.tierT4, ), ), ), diff --git a/app/lib/widgets/evidence_sidebar.dart b/app/lib/widgets/evidence_sidebar.dart index ab4aa7a..070f313 100644 --- a/app/lib/widgets/evidence_sidebar.dart +++ b/app/lib/widgets/evidence_sidebar.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import '../data/models.dart'; -import '../theme/reclaim_tokens.dart'; -import 'reclaim_card.dart'; +import '../theme/lawheatmap_tokens.dart'; +import 'lawheatmap_card.dart'; import 'source_chip.dart'; /// Sidebar listing the citable sources behind an evaluation. @@ -16,39 +16,39 @@ class EvidenceSidebar extends StatelessWidget { @override Widget build(BuildContext context) { final t = Theme.of(context).textTheme; - return ReclaimCard( + return LawHeatmapCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Quellen', style: t.headlineSmall), - const SizedBox(height: ReclaimSpace.sm), + const SizedBox(height: LawHeatmapSpace.sm), Text( 'Jede Zahl trägt die Tier-Stufe ihres schwächsten ' 'Bestandteils (Regel der niedrigsten Stufe, §6.7).', - style: t.labelSmall?.copyWith(color: ReclaimColors.mute), + style: t.labelSmall?.copyWith(color: LawHeatmapColors.mute), ), - const SizedBox(height: ReclaimSpace.lg), + const SizedBox(height: LawHeatmapSpace.lg), for (final s in evaluation.sources) SourceChip(source: s), - const Divider(height: ReclaimSpace.xl), + const Divider(height: LawHeatmapSpace.xl), Text('Audit-Event', style: t.labelLarge), const SizedBox(height: 4), SelectableText( evaluation.auditEventId, style: t.labelSmall?.copyWith( - fontFamily: ReclaimTypography.mono, - color: ReclaimColors.mute, + fontFamily: LawHeatmapTypography.mono, + color: LawHeatmapColors.mute, ), ), - const SizedBox(height: ReclaimSpace.sm), + const SizedBox(height: LawHeatmapSpace.sm), Text( 'Quell-SHA-256:', - style: t.labelSmall?.copyWith(color: ReclaimColors.mute), + style: t.labelSmall?.copyWith(color: LawHeatmapColors.mute), ), SelectableText( evaluation.norm.sourceSha256, style: t.labelSmall?.copyWith( - fontFamily: ReclaimTypography.mono, - color: ReclaimColors.mute, + fontFamily: LawHeatmapTypography.mono, + color: LawHeatmapColors.mute, fontSize: 10, ), ), diff --git a/app/lib/widgets/heatmap_grid.dart b/app/lib/widgets/heatmap_grid.dart index 3abb1a8..bf2fa41 100644 --- a/app/lib/widgets/heatmap_grid.dart +++ b/app/lib/widgets/heatmap_grid.dart @@ -3,7 +3,7 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; import '../data/models.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_tokens.dart'; /// 2D scatter heatmap — x: Schaden (€/Jahr, log-scaled), /// y: Nutzen (0–5), point area: Betroffenheit (sqrt-scaled). @@ -121,7 +121,7 @@ class _HeatmapPainter extends CustomPainter { void paint(Canvas canvas, Size size) { final layout = _Layout(size); final onSurface = theme.colorScheme.onSurface; - const mute = ReclaimColors.mute; + const mute = LawHeatmapColors.mute; // Plot frame. final frame = Paint() @@ -163,7 +163,7 @@ class _HeatmapPainter extends CustomPainter { const axisStyle = TextStyle( color: mute, fontSize: 10, - fontFamily: ReclaimTypography.body, + fontFamily: LawHeatmapTypography.body, ); _drawText( canvas, @@ -205,8 +205,8 @@ class _HeatmapPainter extends CustomPainter { final r = layout.radius(e); final hot = e.skmEurPerYear > 50000000; final color = Color.lerp( - ReclaimColors.benefitSaturated, - ReclaimColors.harmWarm, + LawHeatmapColors.benefitSaturated, + LawHeatmapColors.harmWarm, hot ? 0.5 : 0.0, )!; final body = Paint()..color = color.withValues(alpha: 0.7); @@ -224,7 +224,7 @@ class _HeatmapPainter extends CustomPainter { TextStyle( color: onSurface, fontSize: 11, - fontFamily: ReclaimTypography.body, + fontFamily: LawHeatmapTypography.body, fontWeight: i == hoverIndex ? FontWeight.w600 : FontWeight.w400, ), ); @@ -240,8 +240,8 @@ class _HeatmapPainter extends CustomPainter { ..color = theme.colorScheme.surface.withValues(alpha: 0.92); final boxBorder = Paint() ..style = PaintingStyle.stroke - ..color = ReclaimColors.signal.withValues(alpha: 0.6); - final rrect = RRect.fromRectAndRadius(box, ReclaimRadius.sm); + ..color = LawHeatmapColors.signal.withValues(alpha: 0.6); + final rrect = RRect.fromRectAndRadius(box, LawHeatmapRadius.sm); canvas.drawRRect(rrect, boxPaint); canvas.drawRRect(rrect, boxBorder); _drawText( @@ -252,7 +252,7 @@ class _HeatmapPainter extends CustomPainter { color: onSurface, fontSize: 12, fontWeight: FontWeight.w600, - fontFamily: ReclaimTypography.body, + fontFamily: LawHeatmapTypography.body, ), ); _drawText( @@ -262,7 +262,7 @@ class _HeatmapPainter extends CustomPainter { const TextStyle( color: mute, fontSize: 11, - fontFamily: ReclaimTypography.body, + fontFamily: LawHeatmapTypography.body, ), ); _drawText( @@ -272,7 +272,7 @@ class _HeatmapPainter extends CustomPainter { const TextStyle( color: mute, fontSize: 11, - fontFamily: ReclaimTypography.body, + fontFamily: LawHeatmapTypography.body, ), ); } diff --git a/app/lib/widgets/hub_capabilities_card.dart b/app/lib/widgets/hub_capabilities_card.dart index 3ed4d51..42d644e 100644 --- a/app/lib/widgets/hub_capabilities_card.dart +++ b/app/lib/widgets/hub_capabilities_card.dart @@ -3,8 +3,8 @@ import 'package:flutter/material.dart'; import '../data/hub_repository.dart'; import '../data/repository.dart'; -import '../theme/reclaim_tokens.dart'; -import 'reclaim_card.dart'; +import '../theme/lawheatmap_tokens.dart'; +import 'lawheatmap_card.dart'; /// Lists the capabilities the connected hub has installed. /// Empty / explanatory state when the active repository is not @@ -39,7 +39,7 @@ class _HubCapabilitiesCardState extends State { final repo = widget.repository; final liveHub = repo is HubRepository && repo.isHealthy; - return ReclaimCard( + return LawHeatmapCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -53,13 +53,13 @@ class _HubCapabilitiesCardState extends State { icon: const Icon(Icons.refresh), ), ]), - const SizedBox(height: ReclaimSpace.sm), + const SizedBox(height: LawHeatmapSpace.sm), if (!liveHub) Text( 'Sichtbar sobald die App im Hub-Modus startet und ' 'der Hub erreichbar ist (siehe Verbindung oben).', style: t.labelSmall?.copyWith( - color: ReclaimColors.mute, + color: LawHeatmapColors.mute, ), ) else @@ -68,14 +68,14 @@ class _HubCapabilitiesCardState extends State { builder: (context, snap) { if (snap.connectionState == ConnectionState.waiting) { return const Padding( - padding: EdgeInsets.all(ReclaimSpace.md), + padding: EdgeInsets.all(LawHeatmapSpace.md), child: CircularProgressIndicator(), ); } if (snap.hasError) { return Text('Fehler: ${snap.error}', style: t.labelLarge - ?.copyWith(color: ReclaimColors.harmWarm)); + ?.copyWith(color: LawHeatmapColors.harmWarm)); } final items = snap.data ?? const []; if (items.isEmpty) { @@ -83,7 +83,7 @@ class _HubCapabilitiesCardState extends State { 'Hub hat noch keine Capabilities installiert. ' 'Mit `chain install ` hinzufügen.', style: t.bodyLarge?.copyWith( - color: ReclaimColors.mute, + color: LawHeatmapColors.mute, ), ); } @@ -101,7 +101,7 @@ class _HubCapabilitiesCardState extends State { child: Text( '${c.capability}@${c.version}', style: t.bodyLarge?.copyWith( - fontFamily: ReclaimTypography.mono, + fontFamily: LawHeatmapTypography.mono, ), ), ), @@ -110,7 +110,7 @@ class _HubCapabilitiesCardState extends State { '${c.moduleName} ${c.moduleVersion}' '${c.kind.isEmpty ? "" : " · ${c.kind}"}', style: t.labelSmall?.copyWith( - color: ReclaimColors.mute, + color: LawHeatmapColors.mute, ), ), ), diff --git a/app/lib/widgets/reclaim_card.dart b/app/lib/widgets/lawheatmap_card.dart similarity index 83% rename from app/lib/widgets/reclaim_card.dart rename to app/lib/widgets/lawheatmap_card.dart index 891956f..8a84701 100644 --- a/app/lib/widgets/reclaim_card.dart +++ b/app/lib/widgets/lawheatmap_card.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_tokens.dart'; /// Surface container — visual equivalent of fai_chain_studio's /// `ChainCard`, retuned to the fai_web palette. -class ReclaimCard extends StatelessWidget { - const ReclaimCard({ +class LawHeatmapCard extends StatelessWidget { + const LawHeatmapCard({ super.key, required this.child, this.padding = - const EdgeInsets.all(ReclaimSpace.lg), + const EdgeInsets.all(LawHeatmapSpace.lg), this.accent = false, this.expand = false, }); @@ -39,9 +39,9 @@ class ReclaimCard extends StatelessWidget { Container( height: 2, decoration: const BoxDecoration( - color: ReclaimColors.signal, + color: LawHeatmapColors.signal, borderRadius: BorderRadius.vertical( - top: ReclaimRadius.md, + top: LawHeatmapRadius.md, ), ), ), diff --git a/app/lib/widgets/norm_text_card.dart b/app/lib/widgets/norm_text_card.dart deleted file mode 100644 index 7c98ce2..0000000 --- a/app/lib/widgets/norm_text_card.dart +++ /dev/null @@ -1,143 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../data/models.dart'; -import '../theme/reclaim_tokens.dart'; -import 'reclaim_card.dart'; - -/// Renders the actual paragraphs of a norm right next to the -/// evaluation. Solves the "I want to read the law in place" -/// requirement so a reviewer never has to context-switch to a -/// browser tab just to confirm what the score is about. -/// -/// Two side-buttons at the top: -/// - "Im Browser öffnen" — pop the canonical source URL -/// - "URL kopieren" — clipboard, useful for citations -/// -/// On macOS the browser-open uses the system `open` command — -/// no extra package, no permission prompt. iOS/Linux/Windows -/// branches would need `url_launcher` later; Phase 0 ships -/// macOS first. -class NormTextCard extends StatelessWidget { - const NormTextCard({super.key, required this.norm}); - - final Norm norm; - - @override - Widget build(BuildContext context) { - final t = Theme.of(context).textTheme; - final paragraphs = norm.paragraphs; - return ReclaimCard( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text('Gesetzestext', style: t.headlineSmall), - const SizedBox(height: 4), - Text( - '${norm.jurabk} ${norm.paragraph} — Stand ' - '${_isoDate(norm.standDate)}', - style: t.labelSmall?.copyWith( - color: ReclaimColors.mute, - ), - ), - ], - ), - ), - IconButton( - tooltip: 'URL kopieren', - onPressed: () => - Clipboard.setData(ClipboardData(text: norm.sourceUrl)), - icon: const Icon(Icons.link), - ), - FilledButton.icon( - onPressed: () => _openInBrowser(context, norm.sourceUrl), - icon: const Icon(Icons.open_in_new), - label: const Text('Im Browser öffnen'), - ), - ], - ), - const SizedBox(height: ReclaimSpace.lg), - if (paragraphs.isEmpty) - Text( - 'Für diese Norm ist noch kein normalisierter Text ' - 'eingebunden. Wenn Ch∆In mit `text.akoma_normalize@^0` ' - 'gegen die Quell-URL läuft, erscheinen die Absätze hier.', - style: t.bodyLarge?.copyWith(color: ReclaimColors.mute), - ) - else - for (final p in paragraphs) - Padding( - padding: - const EdgeInsets.only(bottom: ReclaimSpace.md), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: 64, - child: Text( - p.number, - style: t.labelLarge?.copyWith( - color: ReclaimColors.signal, - fontFamily: ReclaimTypography.mono, - ), - ), - ), - Expanded( - child: SelectableText( - p.text, - style: t.bodyLarge?.copyWith(height: 1.55), - ), - ), - ], - ), - ), - const SizedBox(height: ReclaimSpace.sm), - SelectableText( - norm.sourceUrl, - style: t.labelSmall?.copyWith( - color: ReclaimColors.mute, - fontFamily: ReclaimTypography.mono, - ), - ), - ], - ), - ); - } - - String _isoDate(DateTime d) => - '${d.year.toString().padLeft(4, "0")}-' - '${d.month.toString().padLeft(2, "0")}-' - '${d.day.toString().padLeft(2, "0")}'; - - Future _openInBrowser(BuildContext context, String url) async { - final messenger = ScaffoldMessenger.of(context); - final uri = Uri.tryParse(url); - if (uri == null) { - messenger.showSnackBar( - SnackBar(content: Text('Ungültige URL: $url')), - ); - return; - } - try { - final ok = - await launchUrl(uri, mode: LaunchMode.externalApplication); - if (!ok) { - messenger.showSnackBar( - const SnackBar(content: Text('Browser-Aufruf abgelehnt.')), - ); - } - } catch (e) { - messenger.showSnackBar( - SnackBar(content: Text('Konnte Browser nicht öffnen: $e')), - ); - } - } -} diff --git a/app/lib/widgets/source_chip.dart b/app/lib/widgets/source_chip.dart index fdd16c0..ca81fa7 100644 --- a/app/lib/widgets/source_chip.dart +++ b/app/lib/widgets/source_chip.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../data/models.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_tokens.dart'; import 'tier_badge.dart'; /// A citable source line: tier badge + label + (planned) link. @@ -16,7 +16,7 @@ class SourceChip extends StatelessWidget { Widget build(BuildContext context) { final t = Theme.of(context).textTheme; return Padding( - padding: const EdgeInsets.only(bottom: ReclaimSpace.sm), + padding: const EdgeInsets.only(bottom: LawHeatmapSpace.sm), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -24,7 +24,7 @@ class SourceChip extends StatelessWidget { padding: const EdgeInsets.only(top: 2), child: TierBadge(tier: source.tier, compact: true), ), - const SizedBox(width: ReclaimSpace.sm), + const SizedBox(width: LawHeatmapSpace.sm), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -33,14 +33,14 @@ class SourceChip extends StatelessWidget { Text( source.url, style: t.labelSmall?.copyWith( - color: ReclaimColors.mute, + color: LawHeatmapColors.mute, ), ), if (source.note != null) Text( source.note!, style: t.labelSmall?.copyWith( - color: ReclaimColors.mute, + color: LawHeatmapColors.mute, fontStyle: FontStyle.italic, ), ), diff --git a/app/lib/widgets/tier_badge.dart b/app/lib/widgets/tier_badge.dart index 282eed6..b194a6b 100644 --- a/app/lib/widgets/tier_badge.dart +++ b/app/lib/widgets/tier_badge.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../data/models.dart'; -import '../theme/reclaim_tokens.dart'; +import '../theme/lawheatmap_tokens.dart'; /// Tier badge — surfaces the lowest evidence tier in a composed /// value. Every figure in the app carries one of these next to it. @@ -15,22 +15,22 @@ class TierBadge extends StatelessWidget { Widget build(BuildContext context) { final t = Theme.of(context).textTheme; final color = switch (tier) { - EvidenceTier.t1 => ReclaimColors.tierT1, - EvidenceTier.t2 => ReclaimColors.tierT2, - EvidenceTier.t3 => ReclaimColors.tierT3, - EvidenceTier.t4 => ReclaimColors.tierT4, + EvidenceTier.t1 => LawHeatmapColors.tierT1, + EvidenceTier.t2 => LawHeatmapColors.tierT2, + EvidenceTier.t3 => LawHeatmapColors.tierT3, + EvidenceTier.t4 => LawHeatmapColors.tierT4, }; return Tooltip( message: '${tier.short} — ${tier.description}', child: Container( padding: EdgeInsets.symmetric( - horizontal: compact ? 6 : ReclaimSpace.sm, + horizontal: compact ? 6 : LawHeatmapSpace.sm, vertical: compact ? 2 : 4, ), decoration: BoxDecoration( color: color.withValues(alpha: 0.18), border: Border.all(color: color.withValues(alpha: 0.55)), - borderRadius: const BorderRadius.all(ReclaimRadius.sm), + borderRadius: const BorderRadius.all(LawHeatmapRadius.sm), ), child: Row( mainAxisSize: MainAxisSize.min, diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift index 997e35d..724bb2a 100644 --- a/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,9 +6,7 @@ import FlutterMacOS import Foundation import shared_preferences_foundation -import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) - UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/app/macos/Runner.xcodeproj/project.pbxproj b/app/macos/Runner.xcodeproj/project.pbxproj index 00d1a7d..07f152c 100644 --- a/app/macos/Runner.xcodeproj/project.pbxproj +++ b/app/macos/Runner.xcodeproj/project.pbxproj @@ -65,7 +65,7 @@ 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* reclaim_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "reclaim_app.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* lawheatmap_app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "lawheatmap_app.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -134,7 +134,7 @@ 33CC10EE2044A3C60003C045 /* Products */ = { isa = PBXGroup; children = ( - 33CC10ED2044A3C60003C045 /* reclaim_app.app */, + 33CC10ED2044A3C60003C045 /* lawheatmap_app.app */, 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, ); name = Products; @@ -224,7 +224,7 @@ 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, ); productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* reclaim_app.app */; + productReference = 33CC10ED2044A3C60003C045 /* lawheatmap_app.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -395,10 +395,10 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = ai.flemming.reclaim.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = ai.flemming.lawheatmapApp.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/reclaim_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/reclaim_app"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/lawheatmap_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/lawheatmap_app"; }; name = Debug; }; @@ -409,10 +409,10 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = ai.flemming.reclaim.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = ai.flemming.lawheatmapApp.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/reclaim_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/reclaim_app"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/lawheatmap_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/lawheatmap_app"; }; name = Release; }; @@ -423,10 +423,10 @@ CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = ai.flemming.reclaim.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = ai.flemming.lawheatmapApp.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/reclaim_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/reclaim_app"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/lawheatmap_app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/lawheatmap_app"; }; name = Profile; }; diff --git a/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a74a1e8..45c3a4d 100644 --- a/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -15,7 +15,7 @@ @@ -33,7 +33,7 @@ @@ -49,7 +49,7 @@ @@ -84,7 +84,7 @@ @@ -101,7 +101,7 @@ diff --git a/app/macos/Runner/Configs/AppInfo.xcconfig b/app/macos/Runner/Configs/AppInfo.xcconfig index 51f8e2b..3ad4d22 100644 --- a/app/macos/Runner/Configs/AppInfo.xcconfig +++ b/app/macos/Runner/Configs/AppInfo.xcconfig @@ -5,10 +5,10 @@ // 'flutter create' template. // The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = reclaim_app +PRODUCT_NAME = lawheatmap_app // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = ai.flemming.reclaim +PRODUCT_BUNDLE_IDENTIFIER = ai.flemming.lawheatmapApp // The copyright displayed in application information PRODUCT_COPYRIGHT = Copyright © 2026 ai.flemming. All rights reserved. diff --git a/app/macos/Runner/DebugProfile.entitlements b/app/macos/Runner/DebugProfile.entitlements index 08c3ab1..dddb8a3 100644 --- a/app/macos/Runner/DebugProfile.entitlements +++ b/app/macos/Runner/DebugProfile.entitlements @@ -8,7 +8,5 @@ com.apple.security.network.server - com.apple.security.network.client - diff --git a/app/macos/Runner/Release.entitlements b/app/macos/Runner/Release.entitlements index ee95ab7..852fa1a 100644 --- a/app/macos/Runner/Release.entitlements +++ b/app/macos/Runner/Release.entitlements @@ -4,7 +4,5 @@ com.apple.security.app-sandbox - com.apple.security.network.client - diff --git a/app/pubspec.lock b/app/pubspec.lock index c077d58..5cb87e4 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -409,70 +409,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" - url_launcher: - dependency: "direct main" - description: - name: url_launcher - sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 - url: "https://pub.dev" - source: hosted - version: "6.3.2" - url_launcher_android: - dependency: transitive - description: - name: url_launcher_android - sha256: b413d49b73867ac08dd2f9890efd3cc11f2a0e577618d50843440a1fb3776c32 - url: "https://pub.dev" - source: hosted - version: "6.3.32" - url_launcher_ios: - dependency: transitive - description: - name: url_launcher_ios - sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0" - url: "https://pub.dev" - source: hosted - version: "6.4.1" - url_launcher_linux: - dependency: transitive - description: - name: url_launcher_linux - sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a - url: "https://pub.dev" - source: hosted - version: "3.2.2" - url_launcher_macos: - dependency: transitive - description: - name: url_launcher_macos - sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18" - url: "https://pub.dev" - source: hosted - version: "3.2.5" - url_launcher_platform_interface: - dependency: transitive - description: - name: url_launcher_platform_interface - sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - url_launcher_web: - dependency: transitive - description: - name: url_launcher_web - sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34" - url: "https://pub.dev" - source: hosted - version: "2.4.3" - url_launcher_windows: - dependency: transitive - description: - name: url_launcher_windows - sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f" - url: "https://pub.dev" - source: hosted - version: "3.1.5" vector_math: dependency: transitive description: diff --git a/app/pubspec.yaml b/app/pubspec.yaml index c43960b..0eb45b5 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -1,5 +1,5 @@ -name: reclaim_app -description: Recl∆Im — Flutter client for the Ch∆In-based duty-level reform-mapping platform (F∆I). +name: lawheatmap_app +description: F∆I Law-Heatmap — Flutter client for the Ch∆In-based law-bureaucracy heatmap. publish_to: 'none' version: 0.1.0+1 @@ -14,7 +14,6 @@ dependencies: sdk: flutter intl: ^0.20.2 shared_preferences: ^2.3.0 - url_launcher: ^6.3.0 # Ch∆In gRPC wire — sibling-path dep, same pattern as # fai_chain_studio. Adapt the chain_client_sdk_dart relative diff --git a/app/test/widget_test.dart b/app/test/widget_test.dart index eb83a95..873b29f 100644 --- a/app/test/widget_test.dart +++ b/app/test/widget_test.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:reclaim_app/main.dart'; +import 'package:lawheatmap_app/main.dart'; void main() { // Generous surface so NavigationRail + Heatmap fit without @@ -18,9 +18,9 @@ void main() { testWidgets('Landing renders brand mark + CTA', (WidgetTester tester) async { await tester.binding.setSurfaceSize(surfaceSize); - await tester.pumpWidget(const ReclaimApp()); + await tester.pumpWidget(const LawHeatmapApp()); await tester.pump(); - expect(find.text('Recl∆Im'), findsOneWidget); + expect(find.text('F∆I Law-Heatmap'), findsOneWidget); expect( find.widgetWithText(FilledButton, 'App öffnen (Demo-Modus)'), findsOneWidget, @@ -31,7 +31,7 @@ void main() { testWidgets('Tapping CTA reaches the shell and shows the heatmap', (WidgetTester tester) async { await tester.binding.setSurfaceSize(surfaceSize); - await tester.pumpWidget(const ReclaimApp()); + await tester.pumpWidget(const LawHeatmapApp()); await tester.pump(); await tester.tap( find.widgetWithText(FilledButton, 'App öffnen (Demo-Modus)'), diff --git a/flows/durchstich-gewo-14.yaml b/flows/durchstich-gewo-14.yaml index 5b2162d..d7eb63f 100644 --- a/flows/durchstich-gewo-14.yaml +++ b/flows/durchstich-gewo-14.yaml @@ -122,7 +122,7 @@ steps: - id: review use: system.approval@^0 with: - title: "Recl∆Im (F∆I) — Review ${{ normalize.norm.eli }}" + title: "F∆I Law-Heatmap — Review ${{ normalize.norm.eli }}" payload: norm: ${{ normalize.norm }} skm: ${{ skm.report }}