diff --git a/demo_app/pubspec.lock b/demo_app/pubspec.lock index 561d3d8da..08c4b8203 100644 --- a/demo_app/pubspec.lock +++ b/demo_app/pubspec.lock @@ -620,10 +620,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "993381400e94d18469750e5b9dcb8206f15bc09f9da86b9e44a9b0092a0066db" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.18" path_provider_foundation: dependency: transitive description: @@ -945,10 +945,10 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "0aedad096a85b49df2e4725fa32118f9fa580f3b14af7a2d2221896a02cd5656" + sha256: "69ee86740f2847b9a4ba6cffa74ed12ce500bbe2b07f3dc1e643439da60637b7" url: "https://pub.dev" source: hosted - version: "6.3.17" + version: "6.3.18" url_launcher_ios: dependency: transitive description: @@ -1049,10 +1049,10 @@ packages: dependency: transitive description: name: video_player_android - sha256: "53f3b57c7ac88c18e6074d0f94c7146e128c515f0a4503c3061b8e71dea3a0f2" + sha256: "59e5a457ddcc1688f39e9aef0efb62aa845cf0cbbac47e44ac9730dc079a2385" url: "https://pub.dev" source: hosted - version: "2.8.12" + version: "2.8.13" video_player_avfoundation: dependency: transitive description: @@ -1153,10 +1153,10 @@ packages: dependency: transitive description: name: webview_flutter_android - sha256: "0a42444056b24ed832bdf3442d65c5194f6416f7e782152384944053c2ecc9a3" + sha256: "9a25f6b4313978ba1c2cda03a242eea17848174912cfb4d2d8ee84a556f248e3" url: "https://pub.dev" source: hosted - version: "4.10.0" + version: "4.10.1" webview_flutter_platform_interface: dependency: transitive description: diff --git a/packages/core/.gitignore b/packages/core/.gitignore index e9bbc3ca1..e8b4873c9 100644 --- a/packages/core/.gitignore +++ b/packages/core/.gitignore @@ -1,6 +1,9 @@ coverage/ pubspec.lock +# Test failures +test/failures/ + # Miscellaneous *.class *.log diff --git a/packages/core/lib/src/core_widget_factory.dart b/packages/core/lib/src/core_widget_factory.dart index f316c794d..10958f8da 100644 --- a/packages/core/lib/src/core_widget_factory.dart +++ b/packages/core/lib/src/core_widget_factory.dart @@ -383,6 +383,12 @@ class WidgetFactory extends WidgetFactoryResetter with AnchorWidgetFactory { return buildText(tree, resolved, TextSpan(style: textStyle, text: text)); } + // If text is empty and listStyleType is not a predefined type, + // treat it as an empty string literal (no marker) + if (!_isPredefinedListStyleType(listStyleType)) { + return null; + } + switch (listStyleType) { case kCssListStyleTypeCircle: return HtmlListMarker.circle(textStyle); @@ -396,6 +402,23 @@ class WidgetFactory extends WidgetFactoryResetter with AnchorWidgetFactory { } } + /// Checks if the given list style type is a predefined CSS keyword. + bool _isPredefinedListStyleType(String type) { + return const { + kCssListStyleTypeAlphaLower, + kCssListStyleTypeAlphaLatinLower, + kCssListStyleTypeAlphaUpper, + kCssListStyleTypeAlphaLatinUpper, + kCssListStyleTypeCircle, + kCssListStyleTypeDecimal, + kCssListStyleTypeDisc, + kCssListStyleTypeNone, + kCssListStyleTypeRomanLower, + kCssListStyleTypeRomanUpper, + kCssListStyleTypeSquare, + }.contains(type); + } + /// Builds [Padding]. Widget? buildPadding( BuildTree tree, @@ -550,11 +573,20 @@ class WidgetFactory extends WidgetFactoryResetter with AnchorWidgetFactory { final roman = intToRomanNumerals(i); return roman != null ? '$roman.' : ''; case kCssListStyleTypeNone: - default: + case kCssListStyleTypeCircle: + case kCssListStyleTypeDisc: + case kCssListStyleTypeSquare: + // These are handled by geometric markers in buildListMarker return ''; + default: + // For any unrecognized type, treat it as a string literal + // CSS parser has already extracted the content if it was quoted + return type; } } + + /// Returns an [AssetImage]. ImageProvider? imageProviderFromAsset(String url) { final uri = Uri.parse(url); diff --git a/packages/core/test/tag_li_test.dart b/packages/core/test/tag_li_test.dart index 8fe8ab3de..345c83661 100644 --- a/packages/core/test/tag_li_test.dart +++ b/packages/core/test/tag_li_test.dart @@ -602,6 +602,48 @@ Future main() async { ); }); }); + + group('string literals', () { + testWidgets('renders double quoted string', (WidgetTester tester) async { + const html = ''; + final explained = await explain(tester, html); + expect(explained, equals(padding(item('① ', 'Foo')))); + }); + + testWidgets('renders single quoted string', (WidgetTester tester) async { + const html = ""; + final explained = await explain(tester, html); + expect(explained, equals(padding(item('② ', 'Bar')))); + }); + + testWidgets('renders complex string with symbols', (WidgetTester tester) async { + const html = ''; + final explained = await explain(tester, html); + expect(explained, equals(padding(item('★ ', 'Special')))); + }); + + testWidgets('renders empty string', (WidgetTester tester) async { + const html = ''; + final explained = await explain(tester, html); + expect(explained, equals(padding('[RichText:(:Empty)]'))); + }); + + testWidgets('renders string on ordered list', (WidgetTester tester) async { + const html = '
  1. One
  2. Two
'; + final explained = await explain(tester, html); + expect( + explained, + equals( + padding( + list([ + item('Step ', 'One'), + item('Step ', 'Two'), + ]), + ), + ), + ); + }); + }); }); group('padding-inline-start', () {