Miler rider app: surface system, visible design language, backend lifecycle

Design system
- MilerSurface ladder (canvas → working → raised → floating) with MilerPanel
  as layer 1; canvas moved to #DEE3EA so white separates at 1.290:1.
- Visible vocabulary applied across Home, Deliveries, Activity, Account and
  the sheets: hero heads (tabular numeral + small caption, clamped at 1.3x),
  canvas wells for anything that opens, small filled tags for shelf labels,
  demoted placeholders. Recorded in DESIGN_SYSTEM.md §6.
- One icon family: 222 Material glyphs migrated to Lucide; none left outside
  lib/xpress.
- Colour semantics corrected: amber only for what is genuinely owed, brand red
  reserved for the live stop, disabled primaries go neutral rather than pale.

Data and lifecycle
- lib/data/lifecycle.dart reads mutations for what they prove; route_order.dart
  makes admin sequence the single ordering authority; service_day.dart, and
  stop_area.dart rewritten against live Coimbatore addresses (digit-token
  stripping, city stoplist, street suffixes, stammer collapse).
- countLabel states the load once, in bags.

Testing
- 1440 tests passing; golden shot harnesses for Home, Deliveries, Activity,
  sheets and verify, with test/failures/ now gitignored (diff debris).
- New pins: home_gutter_test, stop_area_test, plus updated structural bounds.

Note: this commit also carries pre-existing working-tree deletions that were
present before this work (API_SPEC.md, README.md, demo test fixtures).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 05:40:35 +05:30
parent c8350563d9
commit d7348e253f
387 changed files with 80693 additions and 12272 deletions

View File

@@ -0,0 +1,94 @@
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:miler/views/helpers/constants/Font_constant.dart';
/// Fonts a reference shot needs, loaded once per suite.
///
/// ── Why every shot harness must call this ──
///
/// A golden rendered without a font is a picture of the fallback, and the
/// fallback for an icon font is an empty box. That is not a loud failure — the
/// test passes, the image is written, and a whole page of missing glyphs
/// becomes the reference every future run is compared against.
///
/// It has happened twice. Poppins ships as separate weight files and had to be
/// registered under one family before `w800` headings stopped rendering as
/// smeared SemiBold; then Lucide arrived as a package-declared font the test
/// binding does not resolve at all, and every icon on Activity came out as a
/// hollow square. One loader, so the next family added to the app is added
/// here rather than in five harnesses that will drift apart.
Future<void> loadGoldenFonts() async {
final poppins = FontLoader(FontConstants.fontFamily);
for (final e in const <(String, FontWeight)>[
('Light', FontWeight.w300),
('Regular', FontWeight.w400),
('Medium', FontWeight.w500),
('SemiBold', FontWeight.w600),
('Bold', FontWeight.w700),
('ExtraBold', FontWeight.w800),
('Black', FontWeight.w900),
]) {
final f = File('assets/fonts/Poppins/Poppins-${e.$1}.ttf');
if (f.existsSync()) {
poppins.addFont(f.readAsBytes().then((b) => ByteData.view(b.buffer)));
}
}
await poppins.load();
await _loadIfPresent('MaterialIcons', const [
'/opt/homebrew/share/flutter/bin/cache/artifacts/material_fonts/'
'MaterialIcons-Regular.otf',
]);
// ── The family name is not `Lucide` ──
//
// Its `IconData` carries `fontPackage: 'lucide_icons_flutter'`, and Flutter
// resolves a packaged font under `packages/<package>/<family>`. Registering
// it as plain `Lucide` loads a font nothing ever asks for, which looks
// exactly like not loading it at all: every glyph renders as a hollow box
// and the golden is written anyway.
//
// Both names are registered — the prefixed one is what the widgets resolve,
// and the bare one costs nothing and covers any direct use.
final lucide = _lucideCandidates();
await _loadIfPresent('packages/lucide_icons_flutter/Lucide', lucide);
await _loadIfPresent('Lucide', lucide, quiet: true);
}
Future<void> _loadIfPresent(
String family,
List<String> paths, {
bool quiet = false,
}) async {
for (final p in paths) {
final f = File(p);
if (!f.existsSync()) continue;
final loader = FontLoader(family)
..addFont(f.readAsBytes().then((b) => ByteData.view(b.buffer)));
await loader.load();
return;
}
// Absent is reported, never silent: the alternative is a reference image
// full of empty boxes that nothing ever flags.
if (quiet) return;
// ignore: avoid_print
print('[GOLDEN] $family not found — its glyphs will render as boxes');
}
List<String> _lucideCandidates() {
final home = Platform.environment['HOME'] ?? '';
final root = Directory('$home/.pub-cache/hosted/pub.dev');
if (!root.existsSync()) return const [];
return [
for (final d in root.listSync().whereType<Directory>())
if (d.path
.split(Platform.pathSeparator)
.last
.startsWith('lucide_icons_flutter-'))
'${d.path}/assets/lucide.ttf',
];
}