mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-12-04 19:16:13 +00:00
* 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
300 lines
8.2 KiB
Dart
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);
|
|
}
|