Lucas 670023b8da
fix: add open workspace assertion (#7824)
* fix: add fix workspace assertion

* fix: macos build

* feat: add new token

* feat: change min width of AFButton to 76.0

* chore: update translations

* feat: add verifying button

* feat: set barrierDismissible as false

* chore: install libcurl4-openssl-dev on Linux

* fix: flutter analyze

* chore: bump cloud version to 0.9.45

* fix: ci tests

* fix: home bloc test

* fix: integration test

* fix: integration test

* fix: integration test
2025-04-25 21:40:21 +08:00

300 lines
8.2 KiB
Dart

// ignore_for_file: avoid_print, depend_on_referenced_packages
import 'dart:convert';
import 'dart:io';
import 'package:collection/collection.dart';
void main() {
generatePrimitive();
generateSemantic();
}
void generatePrimitive() {
// 1. Load the JSON file.
final jsonString =
File('script/Primitive.Mode 1.tokens.json').readAsStringSync();
final jsonData = jsonDecode(jsonString) as Map<String, dynamic>;
// 2. Prepare the output code.
final buffer = StringBuffer();
buffer.writeln('''
// ignore_for_file: constant_identifier_names, non_constant_identifier_names
//
// AUTO-GENERATED - DO NOT EDIT DIRECTLY
//
// This file is auto-generated by the generate_theme.dart script
// Generation time: ${DateTime.now().toIso8601String()}
//
// To modify these colors, edit the source JSON files and run the script:
//
// dart run script/generate_theme.dart
//
import 'package:flutter/material.dart';
class AppFlowyPrimitiveTokens {
AppFlowyPrimitiveTokens._();''');
// 3. Process each color category.
jsonData.forEach((categoryName, categoryData) {
categoryData.forEach((tokenName, tokenData) {
processPrimitiveTokenData(
buffer,
tokenData,
'${categoryName}_$tokenName',
);
});
});
buffer.writeln('}');
// 4. Write the output to a Dart file.
final outputFile = File('lib/src/theme/data/appflowy_default/primitive.dart');
outputFile.writeAsStringSync(buffer.toString());
print('Successfully generated ${outputFile.path}');
}
void processPrimitiveTokenData(
StringBuffer buffer,
Map<String, dynamic> tokenData,
final String currentTokenName,
) {
if (tokenData
case {
r'$type': 'color',
r'$value': final String colorValue,
}) {
final dartColorValue = convertColor(colorValue);
final dartTokenName = currentTokenName.replaceAll('-', '_').toCamelCase();
buffer.writeln('''
/// $colorValue
static Color get $dartTokenName => Color(0x$dartColorValue);''');
} else {
tokenData.forEach((key, value) {
if (value is Map<String, dynamic>) {
processPrimitiveTokenData(buffer, value, '${currentTokenName}_$key');
}
});
}
}
void generateSemantic() {
// 1. Load the JSON file.
final lightJsonString =
File('script/Semantic.Light Mode.tokens.json').readAsStringSync();
final darkJsonString =
File('script/Semantic.Dark Mode.tokens.json').readAsStringSync();
final lightJsonData = jsonDecode(lightJsonString) as Map<String, dynamic>;
final darkJsonData = jsonDecode(darkJsonString) as Map<String, dynamic>;
// 2. Prepare the output code.
final buffer = StringBuffer();
buffer.writeln('''
// ignore_for_file: constant_identifier_names, non_constant_identifier_names
//
// AUTO-GENERATED - DO NOT EDIT DIRECTLY
//
// This file is auto-generated by the generate_theme.dart script
// Generation time: ${DateTime.now().toIso8601String()}
//
// To modify these colors, edit the source JSON files and run the script:
//
// dart run script/generate_theme.dart
//
import 'package:appflowy_ui/appflowy_ui.dart';
import 'package:flutter/material.dart';
import '../shared.dart';
class AppFlowyDefaultTheme implements AppFlowyThemeBuilder {''');
// 3. Process light mode semantic tokens
buffer.writeln('''
@override
AppFlowyThemeData light() {
final textStyle = AppFlowyBaseTextStyle();
final borderRadius = AppFlowySharedTokens.buildBorderRadius();
final spacing = AppFlowySharedTokens.buildSpacing();
final shadow = AppFlowySharedTokens.buildShadow(Brightness.light);''');
lightJsonData.forEach((categoryName, categoryData) {
if ([
'Spacing',
'Border_Radius',
'Shadow',
'Badge_Color',
].contains(categoryName)) {
return;
}
final fullCategoryName = "${categoryName}_color_scheme".toCamelCase();
final className = 'AppFlowy${fullCategoryName.toCapitalize()}';
buffer
..writeln()
..writeln(' final $fullCategoryName = $className(');
categoryData.forEach((tokenName, tokenData) {
processSemanticTokenData(buffer, tokenData, tokenName);
});
buffer.writeln(' );');
});
buffer.writeln();
buffer.writeln('''
return AppFlowyThemeData(
textStyle: textStyle,
textColorScheme: textColorScheme,
borderColorScheme: borderColorScheme,
fillColorScheme: fillColorScheme,
surfaceColorScheme: surfaceColorScheme,
backgroundColorScheme: backgroundColorScheme,
iconColorScheme: iconColorScheme,
brandColorScheme: brandColorScheme,
otherColorsColorScheme: otherColorsColorScheme,
borderRadius: borderRadius,
spacing: spacing,
shadow: shadow,
);
}''');
buffer.writeln();
buffer.writeln('''
@override
AppFlowyThemeData dark() {
final textStyle = AppFlowyBaseTextStyle();
final borderRadius = AppFlowySharedTokens.buildBorderRadius();
final spacing = AppFlowySharedTokens.buildSpacing();
final shadow = AppFlowySharedTokens.buildShadow(Brightness.dark);''');
darkJsonData.forEach((categoryName, categoryData) {
if ([
'Spacing',
'Border_Radius',
'Shadow',
'Badge_Color',
].contains(categoryName)) {
return;
}
final fullCategoryName = "${categoryName}_color_scheme".toCamelCase();
final className = 'AppFlowy${fullCategoryName.toCapitalize()}';
buffer
..writeln()
..writeln(' final $fullCategoryName = $className(');
categoryData.forEach((tokenName, tokenData) {
if (tokenData is Map<String, dynamic>) {
processSemanticTokenData(buffer, tokenData, tokenName);
}
});
buffer.writeln(' );');
});
buffer.writeln();
buffer.writeln('''
return AppFlowyThemeData(
textStyle: textStyle,
textColorScheme: textColorScheme,
borderColorScheme: borderColorScheme,
fillColorScheme: fillColorScheme,
surfaceColorScheme: surfaceColorScheme,
backgroundColorScheme: backgroundColorScheme,
iconColorScheme: iconColorScheme,
brandColorScheme: brandColorScheme,
otherColorsColorScheme: otherColorsColorScheme,
borderRadius: borderRadius,
spacing: spacing,
shadow: shadow,
);
}''');
buffer.writeln('}');
// 4. Write the output to a Dart file.
final outputFile = File('lib/src/theme/data/appflowy_default/semantic.dart');
outputFile.writeAsStringSync(buffer.toString());
print('Successfully generated ${outputFile.path}');
}
void processSemanticTokenData(
StringBuffer buffer,
Map<String, dynamic> json,
final String currentTokenName,
) {
if (json
case {
r'$type': 'color',
r'$value': final String value,
}) {
final semanticTokenName =
currentTokenName.replaceAll('-', '_').toCamelCase();
final String colorValueOrPrimitiveToken;
if (value.isColor) {
colorValueOrPrimitiveToken = 'Color(0x${convertColor(value)})';
} else {
final primitiveToken = value
.replaceAll(RegExp(r'\{|\}'), '')
.replaceAll(RegExp(r'\.|-'), '_')
.toCamelCase();
colorValueOrPrimitiveToken = 'AppFlowyPrimitiveTokens.$primitiveToken';
}
buffer.writeln(' $semanticTokenName: $colorValueOrPrimitiveToken,');
} else {
json.forEach((key, value) {
if (value is Map<String, dynamic>) {
processSemanticTokenData(
buffer,
value,
'${currentTokenName}_$key',
);
}
});
}
}
String convertColor(String hexColor) {
String color = hexColor.toUpperCase().replaceAll('#', '');
if (color.length == 6) {
color = 'FF$color'; // Add missing alpha channel
} else if (color.length == 8) {
color = color.substring(6) + color.substring(0, 6); // Rearrange to ARGB
}
return color;
}
extension on String {
String toCamelCase() {
return split('_').mapIndexed((index, part) {
if (index == 0) {
return part.toLowerCase();
} else {
return part[0].toUpperCase() + part.substring(1).toLowerCase();
}
}).join();
}
String toCapitalize() {
if (isEmpty) {
return this;
}
return '${this[0].toUpperCase()}${substring(1)}';
}
bool get isColor =>
startsWith('#') ||
(startsWith('0x') && length == 10) ||
(startsWith('0xFF') && length == 12);
}