From f97cf6eb382c3f5766a68143409be70d860ba72c Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sat, 2 May 2026 16:55:21 -0400 Subject: [PATCH] feat(flutter): token + asset sync from web; gen_tokens.dart shared/fabledsword.tokens.json mirrors web/src/lib/styles/tokens.json; assets/error-copy.json mirrors the web's error-code copy table; assets/svg/album-fallback.svg mirrors the web placeholder. Together these give Flutter the same visual + copy starting point as the SPA. tool/gen_tokens.dart emits lib/theme/tokens.dart from the JSON; the generated file is committed so CI can verify the JSON and the Dart file haven't drifted (gate added in Task 21). tool/sync_shared.sh mkdirs the shared/ + assets/ destinations before copying so the script works on a fresh checkout (the original plan-text shape only mkdir'd assets/svg). pubspec.yaml assets block uncommented now that the three asset paths exist on disk. --- flutter_client/assets/error-copy.json | 16 +++++++ flutter_client/assets/svg/album-fallback.svg | 20 +++++++++ flutter_client/lib/theme/tokens.dart | 27 ++++++++++++ flutter_client/pubspec.yaml | 11 ++--- flutter_client/shared/fabledsword.tokens.json | 34 +++++++++++++++ flutter_client/tool/gen_tokens.dart | 42 +++++++++++++++++++ flutter_client/tool/sync_shared.sh | 13 ++++++ 7 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 flutter_client/assets/error-copy.json create mode 100644 flutter_client/assets/svg/album-fallback.svg create mode 100644 flutter_client/lib/theme/tokens.dart create mode 100644 flutter_client/shared/fabledsword.tokens.json create mode 100644 flutter_client/tool/gen_tokens.dart create mode 100755 flutter_client/tool/sync_shared.sh diff --git a/flutter_client/assets/error-copy.json b/flutter_client/assets/error-copy.json new file mode 100644 index 00000000..a3c3e4cf --- /dev/null +++ b/flutter_client/assets/error-copy.json @@ -0,0 +1,16 @@ +{ + "unknown": "Something went wrong.", + "unauthenticated": "Your session has ended. Please sign in again.", + "connection_refused": "Couldn't reach the server. Check the URL and try again.", + "lidarr_unreachable": "Lidarr is unreachable right now. Try again, or check Admin → Integrations.", + "lidarr_disabled": "Lidarr integration is not enabled.", + "lidarr_auth_failed": "Lidarr authentication failed.", + "lidarr_defaults_incomplete": "Lidarr is missing a default quality profile or root folder. Set them in Admin → Integrations.", + "lidarr_server_error": "Lidarr returned an error. Check Lidarr's logs for the cause.", + "lidarr_rejected": "Lidarr rejected the request. Check the server logs for the field-level reason.", + "lidarr_album_lookup_failed": "Lidarr doesn't recognize this album. Try Resolve or Delete file instead.", + "album_mbid_missing": "This track has no Lidarr album to remove.", + "request_not_pending": "This request is no longer pending.", + "request_not_found": "That request no longer exists.", + "track_not_found": "That track no longer exists." +} diff --git a/flutter_client/assets/svg/album-fallback.svg b/flutter_client/assets/svg/album-fallback.svg new file mode 100644 index 00000000..9720c751 --- /dev/null +++ b/flutter_client/assets/svg/album-fallback.svg @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/flutter_client/lib/theme/tokens.dart b/flutter_client/lib/theme/tokens.dart new file mode 100644 index 00000000..7468012d --- /dev/null +++ b/flutter_client/lib/theme/tokens.dart @@ -0,0 +1,27 @@ +// GENERATED — do not edit. Source: shared/fabledsword.tokens.json +// Run `dart run tool/gen_tokens.dart` to regenerate. +import 'package:flutter/material.dart'; + +class FabledSwordTokens { + static const Color obsidian = Color(0xFF14171A); + static const Color iron = Color(0xFF1E2228); + static const Color slate = Color(0xFF2C313A); + static const Color pewter = Color(0xFF3F4651); + static const Color parchment = Color(0xFFE8E4D8); + static const Color vellum = Color(0xFFC2BFB4); + static const Color ash = Color(0xFF9C9A92); + static const Color moss = Color(0xFF4A5D3F); + static const Color bronze = Color(0xFF8B7355); + static const Color oxblood = Color(0xFF6B2118); + static const Color warning = Color(0xFF8B6F1E); + static const Color error = Color(0xFFC04A1F); + static const Color info = Color(0xFF3D5A6E); + static const Color accent = Color(0xFF4A6B5C); + static const double radiusSm = 4; + static const double radiusMd = 8; + static const double radiusLg = 12; + static const double radiusXl = 16; + static const String fontDisplay = "Fraunces"; + static const String fontBody = "Inter"; + static const String fontMono = "JetBrains Mono"; +} diff --git a/flutter_client/pubspec.yaml b/flutter_client/pubspec.yaml index 940b162f..30e6378d 100644 --- a/flutter_client/pubspec.yaml +++ b/flutter_client/pubspec.yaml @@ -30,10 +30,7 @@ dev_dependencies: flutter: uses-material-design: true - # assets: - # - assets/svg/ - # - assets/error-copy.json - # - shared/fabledsword.tokens.json - # NOTE: Task 5 will sync assets/ + shared/ and uncomment the block above. - # The flutter test runner refuses to build a bundle with missing asset - # paths, even though `flutter pub get` and `flutter analyze` only warn. + assets: + - assets/svg/ + - assets/error-copy.json + - shared/fabledsword.tokens.json diff --git a/flutter_client/shared/fabledsword.tokens.json b/flutter_client/shared/fabledsword.tokens.json new file mode 100644 index 00000000..e8fa04ec --- /dev/null +++ b/flutter_client/shared/fabledsword.tokens.json @@ -0,0 +1,34 @@ +{ + "colors": { + "obsidian": "#14171A", + "iron": "#1E2228", + "slate": "#2C313A", + "pewter": "#3F4651", + "parchment": "#E8E4D8", + "vellum": "#C2BFB4", + "ash": "#9C9A92", + "moss": "#4A5D3F", + "bronze": "#8B7355", + "oxblood": "#6B2118", + "warning": "#8B6F1E", + "error": "#C04A1F", + "info": "#3D5A6E", + "accent": "#4A6B5C" + }, + "radii": { + "sm": "4px", + "md": "8px", + "lg": "12px", + "xl": "16px" + }, + "fonts": { + "display": "Fraunces", + "body": "Inter", + "mono": "JetBrains Mono" + }, + "fontStacks": { + "display": "'Fraunces', Georgia, serif", + "body": "'Inter', system-ui, sans-serif", + "mono": "'JetBrains Mono', ui-monospace, monospace" + } +} diff --git a/flutter_client/tool/gen_tokens.dart b/flutter_client/tool/gen_tokens.dart new file mode 100644 index 00000000..4ec37f7a --- /dev/null +++ b/flutter_client/tool/gen_tokens.dart @@ -0,0 +1,42 @@ +// Reads shared/fabledsword.tokens.json and emits lib/theme/tokens.dart. +// Run via `dart run tool/gen_tokens.dart`. The generated file is +// committed (CI validates it matches the JSON to catch drift). +import 'dart:convert'; +import 'dart:io'; + +void main() { + final jsonFile = File('shared/fabledsword.tokens.json'); + final tokens = jsonDecode(jsonFile.readAsStringSync()) as Map; + final colors = (tokens['colors'] as Map).cast(); + final radii = (tokens['radii'] as Map).cast(); + final fonts = (tokens['fonts'] as Map).cast(); + + final out = StringBuffer() + ..writeln('// GENERATED — do not edit. Source: shared/fabledsword.tokens.json') + ..writeln('// Run `dart run tool/gen_tokens.dart` to regenerate.') + ..writeln("import 'package:flutter/material.dart';") + ..writeln() + ..writeln('class FabledSwordTokens {'); + + colors.forEach((name, hex) { + final value = hex.replaceFirst('#', '0xFF'); + out.writeln(' static const Color ${_camel(name)} = Color($value);'); + }); + + radii.forEach((name, px) { + final v = px.replaceAll('px', ''); + out.writeln(' static const double radius${_pascal(name)} = $v;'); + }); + + fonts.forEach((name, family) { + out.writeln(' static const String font${_pascal(name)} = ${jsonEncode(family)};'); + }); + + out.writeln('}'); + + File('lib/theme/tokens.dart').writeAsStringSync(out.toString()); + stdout.writeln('wrote lib/theme/tokens.dart'); +} + +String _camel(String s) => s; // tokens are already lowerCamel-friendly +String _pascal(String s) => s[0].toUpperCase() + s.substring(1); diff --git a/flutter_client/tool/sync_shared.sh b/flutter_client/tool/sync_shared.sh new file mode 100755 index 00000000..d1931ac0 --- /dev/null +++ b/flutter_client/tool/sync_shared.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Copies shared assets from web/ into flutter_client/. Run before +# `flutter build` and as part of CI. Idempotent. +set -euo pipefail + +cd "$(dirname "$0")/.." + +mkdir -p shared assets/svg +cp ../web/src/lib/styles/tokens.json shared/fabledsword.tokens.json +cp ../web/src/lib/styles/error-copy.json assets/error-copy.json +cp ../web/static/placeholders/album-fallback.svg assets/svg/album-fallback.svg + +echo "shared assets synced from web/"