Skip to content

docs: translate documentation and test data to English#612

Merged
PeterJhongLinksys merged 29 commits intodev-2.0.0from
austin/ui-kit-update
Feb 3, 2026
Merged

docs: translate documentation and test data to English#612
PeterJhongLinksys merged 29 commits intodev-2.0.0from
austin/ui-kit-update

Conversation

@AustinChangLinksys
Copy link
Collaborator

@AustinChangLinksys AustinChangLinksys commented Feb 3, 2026

User description

Summary

  • Translated project documentation, specs, and architectural analysis to English to facilitate internationalization.
  • Translated all Chinese content in test/test_data/.
  • Consolidated AI Assistant documentation into a single architecture file (doc/ai_assistant/router_ai_assistant_architecture.md).
  • Cleaned up redundant documentation and temporary scripts.
  • Updated pubspec.yaml dependencies and formatted code.

Changes

  • Documentation: doc/, specs/, README.md, constitution.md, etc.
  • Test Data: test/test_data/
  • Demo: lib/demo/wcag_report_with_analysis_demo.dart (Translated and fixed syntax error)

PR Type

Enhancement, Tests, Documentation


Description

  • Dashboard UI Kit Major Update: Implemented comprehensive dashboard refactoring with atomic widget components, custom layout support, and A2UI system integration

  • New Atomic Widgets: Added CustomSpeedTest, CustomMasterNodeInfo, CustomWiFiGrid, CustomQuickPanel, and InternetStatusOnly widgets with compact/normal/expanded display modes

  • Fixed Layout Components: Created FixedDashboardWiFiGrid, FixedInternetConnectionWidget, FixedDashboardQuickPanel, and related widgets for fixed dashboard layouts

  • A2UI System Enhancement: Implemented constraint validator, JSON widget loader, data resolver, action handler, and template builder with comprehensive integration tests

  • Dashboard Widget Specifications Refactored: Translated documentation to English, reorganized widget specs with standardWidgets and customWidgets lists, added atomic component specifications with dynamic constraint variations

  • Sliver-based Dashboard View: Implemented drag-and-drop dashboard with edit mode, constraint validation, display mode switching (compact/normal/expanded), and layout persistence

  • Theme Configuration Provider: Added DemoThemeConfigNotifier with Riverpod state management for dynamic theme customization, JSON serialization, and granular color/component overrides

  • WCAG Accessibility Enhancements: Added batch report generator with AI analysis, HTML report generation with health scores and fix suggestions, regression detection, and systemic issue identification

  • Comprehensive Test Coverage: Added 50+ constraint validator tests, 20+ JSON widget loader tests, A2UI system integration tests, WCAG validation examples, and JNAP data resolver edge case tests

  • Code Cleanup: Removed unused imports, consolidated documentation, cleaned up redundant files, and simplified firmware update provider API


Diagram Walkthrough

flowchart LR
  A["Dashboard Refactoring"] --> B["Atomic Widgets"]
  A --> C["Fixed Layout Components"]
  A --> D["Widget Specifications"]
  B --> E["Speed Test, Master Node, WiFi Grid, Quick Panel"]
  C --> F["Internet Status, Quick Panel, WiFi Card, Port Speed"]
  D --> G["standardWidgets + customWidgets"]
  D --> H["Dynamic Constraints"]
  A --> I["A2UI System"]
  I --> J["Constraint Validator"]
  I --> K["JSON Widget Loader"]
  I --> L["Data Resolver"]
  I --> M["Action Handler"]
  A --> N["Sliver Dashboard View"]
  N --> O["Edit Mode + Drag-Drop"]
  N --> P["Display Modes"]
  A --> Q["Theme Configuration"]
  Q --> R["Riverpod State Management"]
  Q --> S["JSON Serialization"]
  A --> T["WCAG Accessibility"]
  T --> U["Batch Report Generator"]
  T --> V["AI Analysis Integration"]
  A --> W["Testing"]
  W --> X["Constraint Validator Tests"]
  W --> Y["A2UI Integration Tests"]
  W --> Z["JNAP Resolver Tests"]
Loading

File Walkthrough

Relevant files
Enhancement
12 files
wcag_batch_with_analysis.dart
Batch WCAG Report Generator with AI Analysis Integration 

test/accessibility/examples/wcag_batch_with_analysis.dart

  • Added comprehensive batch WCAG report generator with integrated AI
    analysis capabilities
  • Implemented HTML report generation with overall compliance metrics,
    health scores, and critical insights
  • Added support for regression detection, systemic issue identification,
    and priority-sorted fix suggestions
  • Included interactive charts, detailed SC-specific analysis, and
    complete styling for professional reporting
+1294/-0
sliver_dashboard_view.dart
Sliver-based Dashboard View with Edit Mode and Constraints

lib/page/dashboard/views/sliver_dashboard_view.dart

  • Implemented drag-and-drop dashboard view using sliver_dashboard
    package
  • Added edit mode with constraint validation for both native and A2UI
    widgets
  • Implemented display mode switching (compact/normal/expanded) with
    layout persistence
  • Added grid optimization, layout settings panel, and visual feedback
    for constraint violations
+652/-0 
instant_admin_view.dart
Firmware Update Provider API Simplification                           

lib/page/instant_admin/views/instant_admin_view.dart

  • Removed unused import of FirmwareUpdateSettings from JNAP models
  • Simplified firmware auto-update state selection using new
    isAutoUpdateEnabled property
  • Updated firmware update policy setter to use new setAutoUpdateEnabled
    method
  • Reduced code complexity by removing direct reference to firmware
    update policy constants
+3/-7     
dashboard_widget_specs.dart
Dashboard widget specifications refactored for atomic components

lib/page/dashboard/models/dashboard_widget_specs.dart

  • Translated Chinese documentation comments to English for
    internationalization
  • Refactored widget specifications: renamed all list to standardWidgets
    and added new customWidgets list for atomic/Bento layout components
  • Added new atomic widget specs (internetStatusOnly, masterNodeInfo,
    ports, speedTest, networkStats, topology) with detailed descriptions
    and constraints
  • Changed height strategies from intrinsic() to strict() with explicit
    minHeightRows values for better layout control
  • Added getPortsSpec() factory method to dynamically adjust port widget
    constraints based on layout state (LAN presence, horizontal/vertical
    orientation)
  • Added constraint variations (_portsNoLanConstraints,
    _portsHorizontalConstraints, _portsVerticalConstraints) for flexible
    port widget sizing
  • Added custom layout duplicates (wifiGridCustom, quickPanelCustom,
    vpnCustom) with isolated constraint configurations
+454/-29
demo_theme_config_provider.dart
Demo theme configuration provider with Riverpod state management

lib/demo/providers/demo_theme_config_provider.dart

  • New file providing dynamic theme configuration state management for
    demo mode using Riverpod
  • Implemented DemoThemeConfig class with support for style, global
    overlay, visual effects, seed color, and granular Material colors
  • Added AppThemeOverrides support for semantic, palette, surface, and
    component-level theme customization
  • Implemented DemoThemeConfigNotifier with methods for
    importing/exporting theme configs as JSON, resetting to defaults, and
    updating individual theme properties
  • Added granular color setters for primary, secondary, tertiary,
    outline, surface, and error colors
  • Implemented component-specific override methods (updateLoaderColors,
    updateSkeletonColors, updateToggleColors, updateToastColors,
    updateTopologyColors)
  • Added JSON serialization/deserialization with hex color encoding for
    theme persistence
+597/-0 
speed_test.dart
Atomic speed test widget for custom dashboard layouts       

lib/page/dashboard/views/components/widgets/atomic/speed_test.dart

  • New atomic widget CustomSpeedTest for displaying speed test results in
    custom/Bento layouts
  • Implemented three display modes: compact (minimal stats with
    controls), normal (vertical meter layout), and expanded (comprehensive
    view with history chart)
  • Added compact view with download/upload stats, divider, and play
    button for running tests
  • Implemented normal view using SpeedTestWidget with configurable meter
    size and result display
  • Implemented expanded view with meter + big stats panel, details panel
    (date, server, ping), and history chart visualization
  • Added _HistoryChart custom painter for rendering speed test history as
    line chart with download/upload trends
  • Added _BigStatsPanel and _DetailsPanel helper widgets for organized
    result display
  • Integrated with health check provider to show real-time test status
    and historical data
+593/-0 
wifi_grid.dart
Fixed layout WiFi grid widget with responsive display modes

lib/page/dashboard/views/components/fixed_layout/wifi_grid.dart

  • New widget FixedDashboardWiFiGrid for displaying WiFi networks in
    fixed dashboard layouts
  • Supports three display modes: compact (vertical list with wrapping),
    normal (2-column grid), expanded (single column with larger cards)
  • Compact view uses Wrap with fixed-width cards for flexible horizontal
    arrangement
  • Normal view uses GridView with responsive column count based on device
    layout (1 or 2 columns)
  • Expanded view uses ListView with separated items for single-column
    layout
  • Implements tooltip visibility management to show only one tooltip at a
    time
  • Calculates dynamic grid heights based on item count and spacing
  • Integrates with dashboard home provider for WiFi data and LAN port
    detection
+200/-0 
internet_status.dart
Add internet connection status widget with multiple display modes

lib/page/dashboard/views/components/widgets/composite/internet_status.dart

  • New widget InternetConnectionWidget displaying internet connection
    status
  • Supports three display modes: compact, normal, and expanded
  • Includes geolocation display, uptime formatting, and refresh
    functionality
  • Integrates with internetStatusProvider and polling mechanisms
+498/-0 
master_node_info.dart
Add master node info widget with system stats display       

lib/page/dashboard/views/components/widgets/atomic/master_node_info.dart

  • New atomic widget CustomMasterNodeInfo for displaying master router
    information
  • Supports compact, normal, and expanded display modes with different
    layouts
  • Includes system stats section with CPU and memory utilization bars
  • Provides uptime display and device information in unified style
+507/-0 
internet_status.dart
Add fixed layout internet connection status widget             

lib/page/dashboard/views/components/fixed_layout/internet_status.dart

  • New widget FixedInternetConnectionWidget for fixed layout dashboard
  • Mirrors InternetConnectionWidget functionality with fixed layout
    approach
  • Supports compact, normal, and expanded display modes
  • Includes geolocation, uptime, and refresh controls
+491/-0 
quick_panel.dart
Add fixed layout quick actions panel widget                           

lib/page/dashboard/views/components/fixed_layout/quick_panel.dart

  • New widget FixedDashboardQuickPanel for quick actions in fixed layout
  • Supports compact, normal, and expanded display modes
  • Includes instant privacy and night mode toggles with confirmation
    dialogs
  • Provides compact icon-only toggles and expanded descriptions
+457/-0 
dashboard_layout_settings_panel.dart
Refactor dashboard layout settings panel with simplified controls

lib/page/dashboard/views/components/settings/dashboard_layout_settings_panel.dart

  • Refactored to simplify layout settings with custom layout toggle
  • Removed complex widget reordering and column span controls
  • Added hidden widgets section with add functionality
  • Integrated with sliverDashboardControllerProvider for layout
    management
+175/-250
Tests
5 files
json_widget_loader_test.dart
JSON Widget Loader Test Suite with Error Handling               

test/page/dashboard/a2ui/loader/json_widget_loader_test.dart

  • Created comprehensive test suite for JsonWidgetLoader with 20+ test
    cases
  • Implemented tests for fallback scenarios, dynamic discovery, partial
    failures, and malformed JSON handling
  • Added helper functions for mocking asset bundles and simulating
    various error conditions
  • Included tests for asset structure validation and resilience to
    network-like errors
+826/-0 
a2ui_constraint_validator_test.dart
Comprehensive test suite for A2UI constraint validation   

test/page/dashboard/a2ui/validator/a2ui_constraint_validator_test.dart

  • New comprehensive test suite for A2UIConstraintValidator with 50+ test
    cases
  • Tests for validateResize() covering minimum/maximum width/height
    violations, multiple violations, edge cases, and large dimensions
  • Tests for validatePlacement() covering negative positions, grid
    boundary violations, overlap detection, adjacent widgets, and multiple
    existing widgets
  • Tests for suggestValidResize() covering dimension clamping to min/max
    constraints and handling of unknown widgets
  • Tests for ValidationResult factory methods (success, violation, error)
    and result properties
  • Tests for WidgetPlacement and ResizeSuggestion data classes with
    string representations
  • Helper function _registerTestWidgets() that registers test widgets
    with various constraint patterns (normal, strict, large)
+598/-0 
a2ui_system_integration_test.dart
A2UI system integration tests for end-to-end validation   

test/page/dashboard/a2ui/integration/a2ui_system_integration_test.dart

  • New integration test suite for A2UI system covering end-to-end widget
    loading, rendering, and validation
  • Tests for widget loading from assets and successful rendering with
    error handling
  • Tests for widget registry integration with loader and validator
  • Tests for data resolution integration with renderer for data binding
  • Tests for constraint validation integration with widget constraints
    and placement validation
  • Tests for error recovery and system resilience (asset loading
    failures, duplicate registrations, provider exceptions)
  • Tests for performance and memory management (registry listener
    notifications, widget renderer resource disposal)
  • Helper function _createTestThemeData() for proper theme setup in tests
+569/-0 
jnap_data_resolver_edge_cases_test.dart
Add comprehensive edge case tests for JNAP data resolver 

test/page/dashboard/a2ui/resolver/jnap_data_resolver_edge_cases_test.dart

  • New comprehensive test file for JnapDataResolver edge cases with 444
    lines
  • Tests device count, node count, WAN status, uptime, SSID resolution,
    and path handling
  • Includes reactive behavior tests and error resilience scenarios
  • Uses project's standard mock notifiers for consistent testing
+444/-0 
a2ui_widget_grid_test.dart
Add A2UI widget grid integration tests                                     

test/page/dashboard/a2ui/integration/a2ui_widget_grid_test.dart

  • New integration test file with 447 lines for A2UI widget grid system
  • Tests preset widgets registration, rendering, and factory integration
  • Validates widget constraints, specifications, and multiple widget
    rendering
  • Includes theme setup and mock action manager configuration
+447/-0 
Documentation
4 files
wcag_html_with_analysis_example.dart
Enhanced HTML Report Generation Example with AI Analysis 

test/accessibility/examples/wcag_html_with_analysis_example.dart

  • Added example demonstrating HTML report generation with AI analysis
    integration
  • Implemented enhanced report styling with health scores, insights, and
    fix suggestions
  • Included regression detection alerts and systemic issue identification
  • Provided complete usage example showing report generation and file
    output
+595/-0 
device_info.dart
Device Info Model Documentation Enhancement                           

lib/core/models/device_info.dart

  • Added comprehensive documentation comment explaining the purpose of
    NodeDeviceInfo class
  • Clarified that this is a UI layer model without JNAP protocol details
  • Referenced JnapDeviceInfoRaw for raw JNAP response handling
+6/-18   
example_validation.dart
WCAG accessibility validation example and demonstration script

test/accessibility/example_validation.dart

  • New example script demonstrating WCAG accessibility validation system
    usage in PrivacyGUI
  • Demo 1: Target size validation for common dashboard components
    (buttons, icons, FAB)
  • Demo 2: Batch validation combining target size, focus order, and
    semantics reporters
  • Demo 3: Report comparison between two versions showing compliance
    improvements and regressions
  • Demo 4: Cache usage demonstration showing performance improvements
    with ReportMemoryCache
  • Generates HTML and Markdown reports exported to reports/accessibility/
    directory
  • Includes helper function _getGitHash() for retrieving current Git
    commit hash
+426/-0 
wcag_analysis_demo.dart
Add WCAG analysis engine demo with multiple scenarios       

lib/demo/wcag_analysis_demo.dart

  • New demo file showcasing WCAG Intelligence Analysis Engine
    capabilities
  • Demonstrates pattern detection, priority calculation, and fix
    suggestions
  • Includes regression detection, systemic issues detection, and
    multi-report analysis
  • Provides priority-based fix workflow examples with detailed output
+365/-0 
Miscellaneous
1 files
device_list_state.dart
Device List State Refactoring and Import Cleanup                 

lib/page/instant_device/providers/device_list_state.dart

  • Removed unnecessary imports (public_member_api_docs,
    sort_constructors_first, collection)
  • Added import for DeviceListItem model from core models
  • Added export statement for DeviceListItem to simplify downstream
    imports
  • Cleaned up code organization and removed unused dependencies
+2/-171 
Configuration changes
1 files
build_config.dart
Add custom layout build configuration flag                             

lib/constants/build_config.dart

  • Added new build configuration flag customLayout with default value
    false
  • Allows environment-based control of custom layout feature
+2/-0     
Additional files
101 files
manual-testing.md +169/-0 
service-decoupling-audit.md +193/-0 
deploy-demo.yml +6/-0     
launch.json +3/-1     
APPGAP_MAPPING.md +70/-70 
README.md +5/-0     
device_count.json +73/-0   
guest_network.json +144/-0 
network_traffic.json +188/-0 
node_count.json +74/-0   
quick_actions.json +206/-0 
router_control.json +203/-0 
system_health.json +266/-0 
wan_status.json +81/-0   
demo_cache_data.json +600/-1838
constitution.md +168/-176
README.md +29/-0   
analysis_engine_demo.md +250/-0 
2026-01_compliance_report.md +91/-0   
implementation_analysis.md +55/-0   
integration_guide.md +677/-0 
testing_guide.md +497/-0 
router_ai_assistant.md +0/-142 
router_ai_assistant_architecture.md +277/-0 
router_assistant_architecture.md +0/-155 
architecture_analysis_2026-01-05.md +0/-727 
architecture_analysis_2026-01-16.md +609/-0 
MIGRATION_TEST_RESULTS.md +0/-1103
REMAINING_TESTS_SUMMARY.md +0/-352 
SCREENSHOT_TEST_COVERAGE.md +0/-239 
SCREEN_SIZE_VERIFICATION_STATUS.md +0/-358 
architecture-violations-detail.md +330/-0 
dashboard_custom_layout_comprehensive_report_en.md +799/-0 
router_component_registry.dart +2/-2     
_constants.dart +1/-0     
defaults.dart +4/-0     
linksys_cloud_repository.dart +2/-2     
linksys_device_cloud_service.dart +2/-2     
device_info_provider.dart +3/-2     
firmware_update_provider.dart +10/-0   
firmware_update_state.dart +4/-0     
session_provider.dart +45/-1   
side_effect_provider.dart +4/-2     
session_service.dart +63/-3   
jnap_device_info_raw.dart +68/-0   
network.dart +1/-1     
router_repository.dart +2/-2     
device_list_item.dart +173/-0 
privacy_settings.dart +139/-0 
get_local_ip.dart +17/-2   
ip_getter.dart +5/-1     
mobile_get_local_ip.dart +5/-4     
web_get_local_ip.dart +7/-5     
demo_app.dart +43/-8   
demo_overrides.dart +5/-0     
demo_router_provider.dart +85/-0   
demo_ui_provider.dart +31/-0   
components_tab.dart +318/-0 
design_tab.dart +107/-0 
palette_tab.dart +167/-0 
status_tab.dart +99/-0   
topology_tab.dart +400/-0 
theme_studio_fab.dart +23/-0   
theme_studio_panel.dart +248/-0 
color_circle.dart +57/-0   
color_picker_dialog.dart +97/-0   
compact_color_picker.dart +47/-0   
section_header.dart +17/-0   
simple_color_picker.dart +188/-0 
wcag_report_with_analysis_demo.dart +289/-0 
demo_theme_settings_fab.dart +0/-353 
app_en.arb +29/-0   
local_network_settings_view.dart +3/-3     
router_command_provider.dart +9/-0     
router_assistant_view.dart +4/-10   
_a2ui.dart +26/-0   
a2ui_action.dart +140/-0 
a2ui_action_handler.dart +358/-0 
a2ui_action_manager.dart +212/-0 
json_widget_loader.dart +207/-0 
a2ui_constraints.dart +90/-0   
a2ui_template.dart +100/-0 
a2ui_widget_definition.dart +94/-0   
preset_widgets.dart +151/-0 
a2ui_widget_registry.dart +90/-0   
a2ui_widget_renderer.dart +104/-0 
template_builder.dart +298/-0 
template_builder_enhanced.dart +361/-0 
data_path_resolver.dart +21/-0   
jnap_data_resolver.dart +208/-0 
a2ui_constraint_validator.dart +282/-0 
a2ui_constraint_validator_provider.dart +11/-0   
dashboard_widget_factory.dart +140/-0 
dashboard_layout_preferences.dart +52/-1   
display_mode.dart +5/-5     
height_strategy.dart +19/-13 
widget_grid_constraints.dart +45/-14 
widget_spec.dart +69/-10 
dashboard_home_state.dart +15/-1   
dashboard_preferences_provider.dart +21/-0   
Additional files not shown

- Create entry point files for 7 modules:
  - ip_getter, url_helper, export_selector, get_log_selector,
    client_type, assign_ip (new entry points)
- Add mobile_assign_ip.dart (no-op implementation for native)
- Update 17 consumer files to use clean single imports
- Add platform-conditional-exports-audit.md documentation

This eliminates exposed conditional imports in consumer files.
Consumers now only need to import the entry point file.

Modules encapsulated:
1. ip_getter (5 consumers)
2. url_helper (5 consumers)
3. export_selector (2 consumers)
4. get_log_selector (1 consumer)
5. client_type (1 consumer)
6. assign_ip (3 consumers)
- Add 6 new atomic widgets for Custom Layout:
  - DashboardInternetStatus (internet status only)
  - DashboardMasterNodeInfo (router details)
  - DashboardPorts (LAN/WAN port status)
  - DashboardSpeedTest (speed test results)
  - DashboardNetworkStats (nodes/devices count)
  - DashboardTopology (mesh tree view)

- Update DashboardWidgetSpecs:
  - Add specs for all 6 atomic widgets
  - Add standardWidgets and customWidgets lists

- Update DashboardLayoutContext:
  - Add atomic widget fields
  - Add _allAtomicWidgets map
  - Add orderedVisibleCustomSpecs getter

- Update CustomDashboardLayoutStrategy:
  - Use orderedVisibleCustomSpecs for Custom Layout

- Update DashboardLayoutSettingsPanel:
  - Show only atomic widgets in Custom mode (no VPN, no composites)
  - Add reorderCustomWidget for proper ordering

- Fix WiFi Grid truncation:
  - Add SingleChildScrollView for overflow handling
  - Increase HeightStrategy to 5.0 for 2-row default height
…us ui

- Implement resize lock for Topology in Expanded mode (8x5 fixed).
- Update Internet Status constraints: Compact MinHeight=1, Normal/Expanded MinHeight=2.
- Fix Internet Status Compact UI: Remove location info, reduce padding (cleaner look).
- Fix SliverDashboardView mapping: Use correct InternetConnectionWidget for internet_status item.
…t functionality

- Metadata: Added description and requirements to WidgetSpec
- IoC: Implemented WidgetRequirement enum for data-driven feature checks (e.g. VPN)
- Layout: Enabled composite widgets (port_and_speed, networks) in custom layout
- Edit Mode: Added 'Cancel' button with full state restoration (layout positions + view modes)
- Settings: Updated Hidden Widgets panel to respect requirements
- Refactor WiFi Grid Compact View to use AppSurface for unified styling
- Replace InkWell with AppInkWell in Topology, Master Node, and WiFi Grid for consistent interaction feedback
- Enable navigation for Topology Compact View and Master Node
- Implement WiFi band toggling in WiFi Grid Compact View
- Update Internet Status refresh icon color
- Optimize dashboard layout and item heights
- Add dynamic Ports widget constraints based on hardware state (hasLanPort, isHorizontalLayout)
- Add minHeightRows/maxHeightRows to Ports constraints for resize validation
- Update LayoutItemFactory to use IoC pattern with WidgetSpecResolver
- Auto-optimize layout on first use and reset
- Add Optimize button to edit toolbar (uses optimizeLayout())
- Reset button only enabled when custom layout is active
- Reset only resets layout positions and display modes, preserves custom layout toggle
- Fix edit mode state sync bugs when resetting or toggling custom layout
- Update default layout positioning to match target design
- Adjust widget constraints: QuickPanel(h=3), MasterNode(h=4), Topology(w=4,h=4)
- Use DashboardOverlay + SliverDashboard + CustomScrollView for proper scroll behavior
- Fix grid background to only appear in dashboard area (not TopBar/Title)
- Add SliverPadding for correct horizontal margins
- Fix WiFi Grid default height (use spec value instead of hardcoded h:2)
- Simplify DashboardHomeView custom layout branch
- TopBar, Edit Toolbar, Title fixed at top with dashboard grid scrollable
- Add custom_layout parameter to deploy-demo workflow
- Add 13 new localization keys for dashboard custom layout
- Replace hardcoded strings with loc(context) calls
- Fix localization tests for dashboard and speed test views
- Fix unused imports and deprecated debugState usage in tests
- Update UI Kit dependency to v2.10.6
- Fix saveLayout() not called after constraint violation correction in resize
- Add empty children guard to prevent crash in InternetConnectionWidget
- Change .then() to .whenComplete() for error resilience in refresh action
This commit refactors the NodeLightSettings feature to follow Clean Architecture principles, decoupling the UI from JNAP data models.

Changes:

- Introduce NodeLightState UI model

- Update NodeLightSettingsService for Model-State conversion

- Update NodeLightSettingsProvider to expose NodeLightState

- Update consumers (NodeDetailView, QuickPanel) to use NodeLightState

- Update Unit and Widget Tests
…ssions

- Move  to shared
- Refactor  to depend on
- Fix regressions in  and
- Fix mocks in
P0 Fixes:
- P0-1: prepare_dashboard_view.dart - Use SessionProvider instead of direct RouterRepository access
- P0-2: pnp_no_internet_connection_view.dart - Add isLoggedIn getter to PnpNotifier, move defaultAdminPassword to shared constants
- P0-3: local_network_settings_view.dart - Use getLocalIp helper with ProviderReader pattern
- P0-4: router_assistant_view.dart - Move provider definition to dedicated provider file

P1 Fixes:
- P1-1: select_network_provider.dart - Create NetworkAvailabilityService to encapsulate router check logic
- P1-2: channelfinder_provider.dart - Move service provider definition to service file

New files:
- lib/constants/defaults.dart - Shared default constants
- lib/page/ai_assistant/providers/router_command_provider.dart
- lib/page/select_network/services/network_availability_service.dart
- test/core/utils/ip_getter_test.dart - Tests for IP getter refactoring
- test/page/instant_setup/providers/pnp_auth_test.dart - Tests for PnP auth logic

Updated architecture violations report to reflect fixed items.
- Create JnapDeviceInfoRaw for JNAP protocol layer (with services field)
- Refactor NodeDeviceInfo as pure UI model (moved to core/models/)
- Add fetchDeviceInfoAndInitializeServices() to encapsulate buildBetterActions
- Remove services parameter from getAdminPasswordAuthStatus()
- Fix login error display by rethrowing JNAPError for countdown data
- Update all related imports and tests
- Remove FirmwareUpdateState import from firmware_update_process_view.dart
- Remove TimeSettings import from instant_admin_view.dart
- Update firmware_update_provider to expose UI-friendly properties
- Clean up firmware_update_state for proper encapsulation

Resolves P2 architecture violations per architecture-violations-detail.md
…nd settings integration

Completed tasks T027-T029. Implemented JSON-based widget loading, fixed async rebuild issues, and integrated A2UI widgets into the Settings Panel with proper badges.
…lidation

- Added new A2UI widget assets: guest_network, network_traffic, quick_actions, router_control, system_health.

- Implemented robust Action handling system (ActionManager, ActionHandler).

- Enhanced TemplateBuilder with better rendering capabilities.

- Added A2UIConstraintValidator for widget validation.

- Extensive test coverage added for actions, assets, integration, and rendering.

- Refactored JNAP data resolver and updated Dashboard factory.
- Translated project documentation, specs, and architectural analysis to English
- Translated all Chinese content in test/test_data/
- Consolidated AI Assistant documentation into single architecture file
- Cleaned up redundant documentation and temporary scripts
- Updated pubspec.yaml dependencies and formatted code
@qodo-code-review
Copy link

qodo-code-review bot commented Feb 3, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
HTML injection

Description: The generated HTML report interpolates unescaped dynamic fields (e.g., batch.metadata.,
report.title, result.name, result.message, and insight.
) directly into HTML, which can
enable stored HTML/JS injection (XSS) when the report is opened in a browser if any of
those values contain markup like <script> or event handlers.
wcag_batch_with_analysis.dart [48-382]

Referred Code
buffer.writeln('<!DOCTYPE html>');
buffer.writeln('<html lang="zh-TW">');
buffer.writeln('<head>');
buffer.writeln('  <meta charset="UTF-8">');
buffer.writeln(
    '  <meta name="viewport" content="width=device-width, initial-scale=1.0">');
buffer.writeln(
    '  <title>WCAG 完整Compliance報告(含AI analysis)- v${batch.metadata.version}</title>');
buffer.writeln(
    '  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>');
buffer.writeln('  <style>');
buffer.writeln(_getEnhancedStyles());
buffer.writeln('  </style>');
buffer.writeln('</head>');
buffer.writeln('<body>');

// === 頁首導航 ===
buffer.writeln('  <div class="header">');
buffer.writeln('    <div class="container">');
buffer.writeln(
    '      <h1>${batch.statusEmoji} WCAG 完整Compliance報告(含AI analysis)</h1>');



 ... (clipped 314 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: 🏷️
Raw error surfaced: The UI displays the raw a2uiLoaderState.error directly to users, which is not robust and
may expose unhelpful/internal exception details instead of graceful, contextual messaging.

Referred Code
// Check if there was an error loading A2UI widgets
if (a2uiLoaderState.hasError) {
  final error = a2uiLoaderState.error;
  return AppCard(
    child: Center(
      child: AppText.bodyMedium('Error loading widgets: $error'),
    ),
  );
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: 🏷️
User sees exception: The user-facing widget error state interpolates $error into UI text, potentially revealing
internal implementation details that should be kept to internal logs.

Referred Code
// Check if there was an error loading A2UI widgets
if (a2uiLoaderState.hasError) {
  final error = a2uiLoaderState.error;
  return AppCard(
    child: Center(
      child: AppText.bodyMedium('Error loading widgets: $error'),
    ),
  );
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: 🏷️
HTML injection risk: The generated HTML report interpolates unescaped dynamic fields (e.g., result.message,
report.title, insight.title/description, and affected component names) directly into HTML,
enabling HTML/script injection if any of those values come from external or
user-controlled sources.

Referred Code
      '            <td><strong>${report.successCriterion}</strong></td>');
  buffer.writeln('            <td>${report.title}</td>');
  buffer.writeln(
      '            <td><span class="level-badge level-${report.level.name}">${report.level.label}</span></td>');
  buffer.writeln(
      '            <td>${report.score.statusEmoji} ${report.score.percentage.toStringAsFixed(1)}%</td>');
  buffer.writeln(
      '            <td>${_getHealthEmoji(analysis.healthScore)} ${(analysis.healthScore * 100).toStringAsFixed(1)}%</td>');
  buffer.writeln(
      '            <td>${analysis.criticalInsights.length > 0 ? '🔴 ${analysis.criticalInsights.length}' : '✅'}</td>');
  buffer.writeln(
      '            <td><a href="#$scId" class="btn-link">查看詳情 →</a></td>');
  buffer.writeln('          </tr>');
}
buffer.writeln('        </tbody>');
buffer.writeln('      </table>');

buffer.writeln('    </section>');

// === AI analysis區塊 ===
buffer



 ... (clipped 227 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

qodo-code-review bot commented Feb 3, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Security
Escape dynamic HTML fields
Suggestion Impact:The HTML generation was updated to output report.title via _escapeHtml(report.title), and _escapeHtml was implemented using dart:convert's htmlEscape for proper escaping.

code diff:

+import 'dart:convert';
 import 'dart:io';
 import 'package:flutter/material.dart';
 import 'package:ui_kit_library/src/foundation/accessibility/accessibility.dart';
 
-/// 為 WcagBatchResult 生成包含AI analysis的完整 HTML 報告
+/// Generate complete HTML report with AI analysis for WcagBatchResult
 String generateFullHtmlWithAnalysis({
   required WcagBatchResult batch,
   WcagBatchResult? previousBatch,
   bool includeFixSuggestions = true,
 }) {
-  // 執行整體AI analysis(跨所有 SC)
+  // Perform overall AI analysis (across all SCs)
   final engine = WcagAnalysisEngine();
   final overallAnalysis = engine.analyzeMultiple(
     batch.reports,
     includeFixSuggestions: includeFixSuggestions,
   );
 
-  // 為每個 SC 執行個別分析
+  // Perform individual analysis for each SC
   final individualAnalyses = <String, AnalysisResult>{};
   for (final report in batch.reports) {
     WcagReport? previousReport;
     if (previousBatch != null) {
-      // 找出對應的前一版本報告
+      // Find corresponding previous version report
       final reportType = report.successCriterion;
       previousReport = previousBatch.reports.cast<WcagReport?>().firstWhere(
             (r) => r?.successCriterion == reportType,
@@ -46,13 +47,13 @@
   final buffer = StringBuffer();
 
   buffer.writeln('<!DOCTYPE html>');
-  buffer.writeln('<html lang="zh-TW">');
+  buffer.writeln('<html lang="en">');
   buffer.writeln('<head>');
   buffer.writeln('  <meta charset="UTF-8">');
   buffer.writeln(
       '  <meta name="viewport" content="width=device-width, initial-scale=1.0">');
   buffer.writeln(
-      '  <title>WCAG 完整Compliance報告(含AI analysis)- v${batch.metadata.version}</title>');
+      '  <title>WCAG Complete Compliance Report (with AI Analysis) - v${batch.metadata.version}</title>');
   buffer.writeln(
       '  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>');
   buffer.writeln('  <style>');
@@ -61,18 +62,18 @@
   buffer.writeln('</head>');
   buffer.writeln('<body>');
 
-  // === 頁首導航 ===
+  // === Header Navigation ===
   buffer.writeln('  <div class="header">');
   buffer.writeln('    <div class="container">');
   buffer.writeln(
-      '      <h1>${batch.statusEmoji} WCAG 完整Compliance報告(含AI analysis)</h1>');
+      '      <h1>${batch.statusEmoji} WCAG Complete Compliance Report (with AI Analysis)</h1>');
   buffer.writeln('      <div class="header-subtitle">');
   buffer.writeln(
       '        Version ${batch.metadata.version} • ${batch.metadata.environment} • ${batch.metadata.timestamp.toString().substring(0, 19)}');
   buffer.writeln('      </div>');
   buffer.writeln('      <div class="nav-links">');
-  buffer.writeln('        <a href="#overview">📊 總覽</a>');
-  buffer.writeln('        <a href="#analysis">🧠 AI analysis</a>');
+  buffer.writeln('        <a href="#overview">📊 Overview</a>');
+  buffer.writeln('        <a href="#analysis">🧠 AI Analysis</a>');
   for (final report in batch.reports) {
     final scId =
         report.successCriterion.replaceAll(' ', '_').replaceAll('.', '_');
@@ -84,18 +85,19 @@
 
   buffer.writeln('  <div class="container">');
 
-  // === 總覽區塊 ===
+  // === Overview Section ===
   buffer.writeln('    <section id="overview" class="section">');
-  buffer.writeln('      <h2>📊 整體總覽</h2>');
-
-  // 關鍵指標卡片
+  buffer.writeln('      <h2>📊 Overall Overview</h2>');
+
+  // Key Metrics Cards
   buffer.writeln('      <div class="metrics-grid">');
 
   buffer.writeln('        <div class="metric-card">');
   buffer.writeln('          <div class="metric-icon">📈</div>');
   buffer.writeln(
       '          <div class="metric-value">${batch.overallCompliance.toStringAsFixed(1)}%</div>');
-  buffer.writeln('          <div class="metric-label">整體Compliance性</div>');
+  buffer
+      .writeln('          <div class="metric-label">Overall Compliance</div>');
   buffer.writeln('        </div>');
 
   buffer.writeln(
@@ -140,9 +142,9 @@
 
   buffer.writeln('      </div>');
 
-  // 元數據資訊
+  // Metadata Info
   buffer.writeln('      <div class="metadata-card">');
-  buffer.writeln('        <h3>報告資訊</h3>');
+  buffer.writeln('        <h3>Report Information</h3>');
   buffer.writeln('        <div class="metadata-grid">');
   buffer.writeln(
       '          <div><strong>Git Commit:</strong> ${batch.metadata.gitCommitHash}</div>');
@@ -159,34 +161,34 @@
   buffer.writeln('        </div>');
   buffer.writeln('      </div>');
 
-  // 圖表區域
+  // Charts Area
   buffer.writeln('      <div class="charts-row">');
   buffer.writeln('        <div class="chart-card">');
-  buffer.writeln('          <h3>Compliance性分布</h3>');
+  buffer.writeln('          <h3>Compliance Distribution</h3>');
   buffer.writeln('          <div class="chart-container">');
   buffer.writeln('            <canvas id="overallChart"></canvas>');
   buffer.writeln('          </div>');
   buffer.writeln('        </div>');
   buffer.writeln('        <div class="chart-card">');
-  buffer.writeln('          <h3>各 SC Compliance Rate</h3>');
+  buffer.writeln('          <h3>SC Compliance Rate</h3>');
   buffer.writeln('          <div class="chart-container">');
   buffer.writeln('            <canvas id="complianceChart"></canvas>');
   buffer.writeln('          </div>');
   buffer.writeln('        </div>');
   buffer.writeln('      </div>');
 
-  // SC 總覽表格
-  buffer.writeln('      <h3>Success Criteria 詳情</h3>');
+  // SC Overview Table
+  buffer.writeln('      <h3>Success Criteria Details</h3>');
   buffer.writeln('      <table class="sc-table">');
   buffer.writeln('        <thead>');
   buffer.writeln('          <tr>');
   buffer.writeln('            <th>SC</th>');
-  buffer.writeln('            <th>標題</th>');
-  buffer.writeln('            <th>等級</th>');
-  buffer.writeln('            <th>Compliance性</th>');
+  buffer.writeln('            <th>Title</th>');
+  buffer.writeln('            <th>Level</th>');
+  buffer.writeln('            <th>Compliance</th>');
   buffer.writeln('            <th>Health Score</th>');
-  buffer.writeln('            <th>關鍵問題</th>');
-  buffer.writeln('            <th>操作</th>');
+  buffer.writeln('            <th>Critical Issues</th>');
+  buffer.writeln('            <th>Action</th>');
   buffer.writeln('          </tr>');
   buffer.writeln('        </thead>');
   buffer.writeln('        <tbody>');
@@ -197,7 +199,7 @@
     buffer.writeln('          <tr>');
     buffer.writeln(
         '            <td><strong>${report.successCriterion}</strong></td>');
-    buffer.writeln('            <td>${report.title}</td>');
+    buffer.writeln('            <td>${_escapeHtml(report.title)}</td>');
     buffer.writeln(
         '            <td><span class="level-badge level-${report.level.name}">${report.level.label}</span></td>');
     buffer.writeln(
@@ -207,7 +209,7 @@
     buffer.writeln(
         '            <td>${analysis.criticalInsights.length > 0 ? '🔴 ${analysis.criticalInsights.length}' : '✅'}</td>');
     buffer.writeln(
-        '            <td><a href="#$scId" class="btn-link">查看詳情 →</a></td>');
+        '            <td><a href="#$scId" class="btn-link">View Details →</a></td>');
     buffer.writeln('          </tr>');
   }
   buffer.writeln('        </tbody>');
@@ -215,10 +217,10 @@
 
   buffer.writeln('    </section>');
 
-  // === AI analysis區塊 ===
+  // === AI Analysis Section ===
   buffer
       .writeln('    <section id="analysis" class="section analysis-section">');
-  buffer.writeln('      <h2>🧠 AI analysis:整體洞察</h2>');
+  buffer.writeln('      <h2>🧠 AI Analysis: Overall Insights</h2>');
 
   // Regression Warning
   if (overallAnalysis.regressions.isNotEmpty) {
@@ -231,7 +233,7 @@
       buffer.writeln('          <li>');
       buffer.writeln('            <strong>${regression.title}</strong><br>');
       buffer.writeln(
-          '            <span class="text-muted">受影響:${regression.affectedComponents.join(", ")}</span>');
+          '            <span class="text-muted">Affected: ${regression.affectedComponents.join(", ")}</span>');
       buffer.writeln('          </li>');
     }
     buffer.writeln('        </ul>');
@@ -241,8 +243,9 @@
   // Systemic
   if (overallAnalysis.systemicIssues.isNotEmpty) {
     buffer.writeln('      <div class="alert alert-warning">');
-    buffer.writeln('        <h3>⚠️ Systemic</h3>');
-    buffer.writeln('        <p>以下元件在多個 Success Criteria 或情境中Failed:</p>');
+    buffer.writeln('        <h3>⚠️ Systemic Issues Detected!</h3>');
+    buffer.writeln(
+        '        <p>The following components failed across multiple Success Criteria or scenarios:</p>');
     buffer.writeln('        <ul>');
     for (final systemic in overallAnalysis.systemicIssues) {
       buffer.writeln('          <li>');
@@ -256,16 +259,16 @@
     buffer.writeln('      </div>');
   }
 
-  // priority sorting的洞察
-  buffer.writeln('      <h3>💡 優先修復順序(跨所有 SC)</h3>');
-  buffer.writeln(
-      '      <p class="section-subtitle">根據Severity、影響範圍和 WCAG 等級自動排序</p>');
+  // Priority Sorting Insights
+  buffer.writeln('      <h3>💡 Priority Fix Order (Across All SCs)</h3>');
+  buffer.writeln(
+      '      <p class="section-subtitle">Automatically sorted by Severity, Scope, and WCAG Level</p>');
 
   if (overallAnalysis.insights.isEmpty) {
     buffer.writeln('      <div class="success-message">');
     buffer.writeln('        <div style="font-size: 64px;">✅</div>');
-    buffer.writeln('        <h3>沒有發現問題模式!</h3>');
-    buffer.writeln('        <p>All tests comply with WCAG 標準。</p>');
+    buffer.writeln('        <h3>No problem patterns detected!</h3>');
+    buffer.writeln('        <p>All tests comply with WCAG standards.</p>');
     buffer.writeln('      </div>');
   } else {
     for (var i = 0; i < overallAnalysis.insights.length; i++) {
@@ -276,7 +279,7 @@
 
   buffer.writeln('    </section>');
 
-  // === 各 SC 詳細報告 ===
+  // === Individual SC Reports ===
   for (final report in batch.reports) {
     final scId =
         report.successCriterion.replaceAll(' ', '_').replaceAll('.', '_');
@@ -284,7 +287,7 @@
 
     buffer.writeln('    <section id="$scId" class="section sc-section">');
 
-    // SC 標題
+    // SC Title
     buffer.writeln('      <div class="sc-header">');
     buffer.writeln('        <div>');
     buffer.writeln(
@@ -293,20 +296,21 @@
     buffer.writeln(
         '            <span class="level-badge level-${report.level.name}">${report.level.label}</span>');
     buffer.writeln(
-        '            <span>Compliance性: ${report.score.statusEmoji} ${report.score.percentage.toStringAsFixed(1)}%</span>');
+        '            <span>Compliance: ${report.score.statusEmoji} ${report.score.percentage.toStringAsFixed(1)}%</span>');
     buffer.writeln(
         '            <span>Health Score: ${_getHealthEmoji(analysis.healthScore)} ${(analysis.healthScore * 100).toStringAsFixed(1)}%</span>');
     buffer.writeln('          </div>');
     buffer.writeln('        </div>');
-    buffer.writeln('        <a href="#overview" class="back-link">↑ 返回總覽</a>');
+    buffer.writeln(
+        '        <a href="#overview" class="back-link">↑ Back to Overview</a>');
     buffer.writeln('      </div>');
 
-    // SC 統計卡片
+    // SC Stats Card
     buffer.writeln('      <div class="sc-stats-grid">');
     buffer.writeln('        <div class="stat-card">');
     buffer.writeln(
         '          <div class="stat-value">${report.score.passed}/${report.score.total}</div>');
-    buffer.writeln('          <div class="stat-label">Passed測試</div>');
+    buffer.writeln('          <div class="stat-label">Passed Tests</div>');
     buffer.writeln('        </div>');
     buffer.writeln(
         '        <div class="stat-card ${report.criticalFailures.isNotEmpty ? 'critical' : ''}">');
@@ -317,19 +321,19 @@
     buffer.writeln('        <div class="stat-card">');
     buffer.writeln(
         '          <div class="stat-value">${analysis.insights.length}</div>');
-    buffer.writeln('          <div class="stat-label">發現洞察</div>');
+    buffer.writeln('          <div class="stat-label">Insights Found</div>');
     buffer.writeln('        </div>');
     buffer.writeln('        <div class="stat-card">');
     buffer.writeln(
         '          <div class="stat-value">${analysis.estimatedEffort?.toStringAsFixed(1) ?? 'N/A'}h</div>');
-    buffer.writeln('          <div class="stat-label">修復工作量</div>');
+    buffer.writeln('          <div class="stat-label">Fix Effort</div>');
     buffer.writeln('        </div>');
     buffer.writeln('      </div>');
 
-    // SC 特定的AI analysis
+    // SC Specific AI Analysis
     if (analysis.insights.isNotEmpty) {
       buffer.writeln('      <div class="sc-analysis">');
-      buffer.writeln('        <h3>🧠 此 SC 的AI analysis</h3>');
+      buffer.writeln('        <h3>🧠 AI Analysis for this SC</h3>');
       for (var i = 0; i < analysis.insights.length; i++) {
         buffer.write(
             _generateInsightCard(analysis.insights[i], i + 1, compact: true));
@@ -337,16 +341,16 @@
       buffer.writeln('      </div>');
     }
 
-    // 測試結果表格
+    // Test Results Table
     if (report.results.isNotEmpty) {
-      buffer.writeln('      <h3>📋 測試結果</h3>');
+      buffer.writeln('      <h3>📋 Test Results</h3>');
       buffer.writeln('      <table class="results-table">');
       buffer.writeln('        <thead>');
       buffer.writeln('          <tr>');
-      buffer.writeln('            <th>元件</th>');
-      buffer.writeln('            <th>狀態</th>');
-      buffer.writeln('            <th>嚴重性</th>');
-      buffer.writeln('            <th>說明</th>');
+      buffer.writeln('            <th>Component</th>');
+      buffer.writeln('            <th>Status</th>');
+      buffer.writeln('            <th>Severity</th>');
+      buffer.writeln('            <th>Description</th>');
       buffer.writeln('          </tr>');
       buffer.writeln('        </thead>');
       buffer.writeln('        <tbody>');
@@ -381,7 +385,7 @@
   return buffer.toString();
 }
 
-/// 生成洞察卡片 HTML
+/// Generate Insight Card HTML
 String _generateInsightCard(Insight insight, int priority,
     {bool compact = false}) {
   final severityClass = 'insight-${insight.severity.name}';
@@ -390,7 +394,7 @@
       <div class="insight-card $severityClass">
         <div class="insight-header">
           <div class="insight-priority">
-            <span class="priority-badge">優先級 $priority</span>
+            <span class="priority-badge">Priority $priority</span>
           </div>
           <div class="insight-title-group">
             <span class="insight-emoji">${insight.severity.emoji}</span>
@@ -413,7 +417,7 @@
             </div>
             <div class="detail-item">
               <span class="detail-icon">🔢</span>
-              <span class="detail-label">Failed次數:</span>
+              <span class="detail-label">Failure Count:</span>
               <span class="detail-value">${insight.failureCount}</span>
             </div>
           </div>
@@ -446,10 +450,10 @@
 ''';
 }
 
-/// 生成圖表 JavaScript
+/// Generate Chart JavaScript
 String _generateChartScript(WcagBatchResult batch) {
   return '''
-    // 整體Compliance Doughnut Chart
+    // Overall Compliance Doughnut Chart
     new Chart(document.getElementById('overallChart'), {
       type: 'doughnut',
       data: {
@@ -475,15 +479,15 @@
       }
     });
 
-    // 各 SC Compliance Rate長條圖
+    // SC Compliance Rate Bar Chart
     new Chart(document.getElementById('complianceChart'), {
       type: 'bar',
       data: {
-        labels: [${batch.reports.map((r) => '"${r.successCriterion}"').join(', ')}],
+        labels: ${jsonEncode(batch.reports.map((r) => r.successCriterion).toList())},
         datasets: [{
           label: 'Compliance Rate (%)',
-          data: [${batch.reports.map((r) => r.score.percentage).join(', ')}],
-          backgroundColor: [${batch.reports.map((r) => _getBarColor(r.score.percentage)).join(', ')}],
+          data: ${jsonEncode(batch.reports.map((r) => r.score.percentage).toList())},
+          backgroundColor: ${jsonEncode(batch.reports.map((r) => _getBarColor(r.score.percentage)).toList())},
           borderWidth: 0
         }]
       },
@@ -511,14 +515,14 @@
   ''';
 }
 
-/// 取得長條圖顏色
+/// Get Bar Color
 String _getBarColor(double percentage) {
   if (percentage >= 95) return "'#28a745'";
   if (percentage >= 80) return "'#ffc107'";
   return "'#dc3545'";
 }
 
-/// 取得Health Score的卡片 class
+/// Get Health Score Card Class
 String _getHealthCardClass(double score) {
   if (score >= 0.8) return 'success';
   if (score >= 0.5) return 'warning';
@@ -546,15 +550,10 @@
 
 /// HTML Escape
 String _escapeHtml(String text) {
-  return text
-      .replaceAll('&', '&amp;')
-      .replaceAll('<', '&lt;')
-      .replaceAll('>', '&gt;')
-      .replaceAll('"', '&quot;')
-      .replaceAll("'", '&#39;');
+  return htmlEscape.convert(text);
 }

Wrap the report.title variable with the _escapeHtml utility to prevent a
potential HTML injection vulnerability in the generated report.

test/accessibility/examples/wcag_batch_with_analysis.dart [200]

-buffer.writeln('            <td>${report.title}</td>');
+buffer.writeln('            <td>${_escapeHtml(report.title)}</td>');

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical XSS vulnerability where unescaped data is rendered in the HTML report and provides a precise, minimal fix.

High
Prevent XSS by using robust escaping
Suggestion Impact:Added `import 'dart:convert';` and replaced the manual `_escapeHtml` implementation with `htmlEscape.convert(text)` (also applied escaping when writing `report.title` into HTML).

code diff:

+import 'dart:convert';
 import 'dart:io';
 import 'package:flutter/material.dart';
 import 'package:ui_kit_library/src/foundation/accessibility/accessibility.dart';
 
-/// 為 WcagBatchResult 生成包含AI analysis的完整 HTML 報告
+/// Generate complete HTML report with AI analysis for WcagBatchResult
 String generateFullHtmlWithAnalysis({
   required WcagBatchResult batch,
   WcagBatchResult? previousBatch,
   bool includeFixSuggestions = true,
 }) {
-  // 執行整體AI analysis(跨所有 SC)
+  // Perform overall AI analysis (across all SCs)
   final engine = WcagAnalysisEngine();
   final overallAnalysis = engine.analyzeMultiple(
     batch.reports,
     includeFixSuggestions: includeFixSuggestions,
   );
 
-  // 為每個 SC 執行個別分析
+  // Perform individual analysis for each SC
   final individualAnalyses = <String, AnalysisResult>{};
   for (final report in batch.reports) {
     WcagReport? previousReport;
     if (previousBatch != null) {
-      // 找出對應的前一版本報告
+      // Find corresponding previous version report
       final reportType = report.successCriterion;
       previousReport = previousBatch.reports.cast<WcagReport?>().firstWhere(
             (r) => r?.successCriterion == reportType,
@@ -46,13 +47,13 @@
   final buffer = StringBuffer();
 
   buffer.writeln('<!DOCTYPE html>');
-  buffer.writeln('<html lang="zh-TW">');
+  buffer.writeln('<html lang="en">');
   buffer.writeln('<head>');
   buffer.writeln('  <meta charset="UTF-8">');
   buffer.writeln(
       '  <meta name="viewport" content="width=device-width, initial-scale=1.0">');
   buffer.writeln(
-      '  <title>WCAG 完整Compliance報告(含AI analysis)- v${batch.metadata.version}</title>');
+      '  <title>WCAG Complete Compliance Report (with AI Analysis) - v${batch.metadata.version}</title>');
   buffer.writeln(
       '  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>');
   buffer.writeln('  <style>');
@@ -61,18 +62,18 @@
   buffer.writeln('</head>');
   buffer.writeln('<body>');
 
-  // === 頁首導航 ===
+  // === Header Navigation ===
   buffer.writeln('  <div class="header">');
   buffer.writeln('    <div class="container">');
   buffer.writeln(
-      '      <h1>${batch.statusEmoji} WCAG 完整Compliance報告(含AI analysis)</h1>');
+      '      <h1>${batch.statusEmoji} WCAG Complete Compliance Report (with AI Analysis)</h1>');
   buffer.writeln('      <div class="header-subtitle">');
   buffer.writeln(
       '        Version ${batch.metadata.version} • ${batch.metadata.environment} • ${batch.metadata.timestamp.toString().substring(0, 19)}');
   buffer.writeln('      </div>');
   buffer.writeln('      <div class="nav-links">');
-  buffer.writeln('        <a href="#overview">📊 總覽</a>');
-  buffer.writeln('        <a href="#analysis">🧠 AI analysis</a>');
+  buffer.writeln('        <a href="#overview">📊 Overview</a>');
+  buffer.writeln('        <a href="#analysis">🧠 AI Analysis</a>');
   for (final report in batch.reports) {
     final scId =
         report.successCriterion.replaceAll(' ', '_').replaceAll('.', '_');
@@ -84,18 +85,19 @@
 
   buffer.writeln('  <div class="container">');
 
-  // === 總覽區塊 ===
+  // === Overview Section ===
   buffer.writeln('    <section id="overview" class="section">');
-  buffer.writeln('      <h2>📊 整體總覽</h2>');
-
-  // 關鍵指標卡片
+  buffer.writeln('      <h2>📊 Overall Overview</h2>');
+
+  // Key Metrics Cards
   buffer.writeln('      <div class="metrics-grid">');
 
   buffer.writeln('        <div class="metric-card">');
   buffer.writeln('          <div class="metric-icon">📈</div>');
   buffer.writeln(
       '          <div class="metric-value">${batch.overallCompliance.toStringAsFixed(1)}%</div>');
-  buffer.writeln('          <div class="metric-label">整體Compliance性</div>');
+  buffer
+      .writeln('          <div class="metric-label">Overall Compliance</div>');
   buffer.writeln('        </div>');
 
   buffer.writeln(
@@ -140,9 +142,9 @@
 
   buffer.writeln('      </div>');
 
-  // 元數據資訊
+  // Metadata Info
   buffer.writeln('      <div class="metadata-card">');
-  buffer.writeln('        <h3>報告資訊</h3>');
+  buffer.writeln('        <h3>Report Information</h3>');
   buffer.writeln('        <div class="metadata-grid">');
   buffer.writeln(
       '          <div><strong>Git Commit:</strong> ${batch.metadata.gitCommitHash}</div>');
@@ -159,34 +161,34 @@
   buffer.writeln('        </div>');
   buffer.writeln('      </div>');
 
-  // 圖表區域
+  // Charts Area
   buffer.writeln('      <div class="charts-row">');
   buffer.writeln('        <div class="chart-card">');
-  buffer.writeln('          <h3>Compliance性分布</h3>');
+  buffer.writeln('          <h3>Compliance Distribution</h3>');
   buffer.writeln('          <div class="chart-container">');
   buffer.writeln('            <canvas id="overallChart"></canvas>');
   buffer.writeln('          </div>');
   buffer.writeln('        </div>');
   buffer.writeln('        <div class="chart-card">');
-  buffer.writeln('          <h3>各 SC Compliance Rate</h3>');
+  buffer.writeln('          <h3>SC Compliance Rate</h3>');
   buffer.writeln('          <div class="chart-container">');
   buffer.writeln('            <canvas id="complianceChart"></canvas>');
   buffer.writeln('          </div>');
   buffer.writeln('        </div>');
   buffer.writeln('      </div>');
 
-  // SC 總覽表格
-  buffer.writeln('      <h3>Success Criteria 詳情</h3>');
+  // SC Overview Table
+  buffer.writeln('      <h3>Success Criteria Details</h3>');
   buffer.writeln('      <table class="sc-table">');
   buffer.writeln('        <thead>');
   buffer.writeln('          <tr>');
   buffer.writeln('            <th>SC</th>');
-  buffer.writeln('            <th>標題</th>');
-  buffer.writeln('            <th>等級</th>');
-  buffer.writeln('            <th>Compliance性</th>');
+  buffer.writeln('            <th>Title</th>');
+  buffer.writeln('            <th>Level</th>');
+  buffer.writeln('            <th>Compliance</th>');
   buffer.writeln('            <th>Health Score</th>');
-  buffer.writeln('            <th>關鍵問題</th>');
-  buffer.writeln('            <th>操作</th>');
+  buffer.writeln('            <th>Critical Issues</th>');
+  buffer.writeln('            <th>Action</th>');
   buffer.writeln('          </tr>');
   buffer.writeln('        </thead>');
   buffer.writeln('        <tbody>');
@@ -197,7 +199,7 @@
     buffer.writeln('          <tr>');
     buffer.writeln(
         '            <td><strong>${report.successCriterion}</strong></td>');
-    buffer.writeln('            <td>${report.title}</td>');
+    buffer.writeln('            <td>${_escapeHtml(report.title)}</td>');
     buffer.writeln(
         '            <td><span class="level-badge level-${report.level.name}">${report.level.label}</span></td>');
     buffer.writeln(
@@ -207,7 +209,7 @@
     buffer.writeln(
         '            <td>${analysis.criticalInsights.length > 0 ? '🔴 ${analysis.criticalInsights.length}' : '✅'}</td>');
     buffer.writeln(
-        '            <td><a href="#$scId" class="btn-link">查看詳情 →</a></td>');
+        '            <td><a href="#$scId" class="btn-link">View Details →</a></td>');
     buffer.writeln('          </tr>');
   }
   buffer.writeln('        </tbody>');
@@ -215,10 +217,10 @@
 
   buffer.writeln('    </section>');
 
-  // === AI analysis區塊 ===
+  // === AI Analysis Section ===
   buffer
       .writeln('    <section id="analysis" class="section analysis-section">');
-  buffer.writeln('      <h2>🧠 AI analysis:整體洞察</h2>');
+  buffer.writeln('      <h2>🧠 AI Analysis: Overall Insights</h2>');
 
   // Regression Warning
   if (overallAnalysis.regressions.isNotEmpty) {
@@ -231,7 +233,7 @@
       buffer.writeln('          <li>');
       buffer.writeln('            <strong>${regression.title}</strong><br>');
       buffer.writeln(
-          '            <span class="text-muted">受影響:${regression.affectedComponents.join(", ")}</span>');
+          '            <span class="text-muted">Affected: ${regression.affectedComponents.join(", ")}</span>');
       buffer.writeln('          </li>');
     }
     buffer.writeln('        </ul>');
@@ -241,8 +243,9 @@
   // Systemic
   if (overallAnalysis.systemicIssues.isNotEmpty) {
     buffer.writeln('      <div class="alert alert-warning">');
-    buffer.writeln('        <h3>⚠️ Systemic</h3>');
-    buffer.writeln('        <p>以下元件在多個 Success Criteria 或情境中Failed:</p>');
+    buffer.writeln('        <h3>⚠️ Systemic Issues Detected!</h3>');
+    buffer.writeln(
+        '        <p>The following components failed across multiple Success Criteria or scenarios:</p>');
     buffer.writeln('        <ul>');
     for (final systemic in overallAnalysis.systemicIssues) {
       buffer.writeln('          <li>');
@@ -256,16 +259,16 @@
     buffer.writeln('      </div>');
   }
 
-  // priority sorting的洞察
-  buffer.writeln('      <h3>💡 優先修復順序(跨所有 SC)</h3>');
-  buffer.writeln(
-      '      <p class="section-subtitle">根據Severity、影響範圍和 WCAG 等級自動排序</p>');
+  // Priority Sorting Insights
+  buffer.writeln('      <h3>💡 Priority Fix Order (Across All SCs)</h3>');
+  buffer.writeln(
+      '      <p class="section-subtitle">Automatically sorted by Severity, Scope, and WCAG Level</p>');
 
   if (overallAnalysis.insights.isEmpty) {
     buffer.writeln('      <div class="success-message">');
     buffer.writeln('        <div style="font-size: 64px;">✅</div>');
-    buffer.writeln('        <h3>沒有發現問題模式!</h3>');
-    buffer.writeln('        <p>All tests comply with WCAG 標準。</p>');
+    buffer.writeln('        <h3>No problem patterns detected!</h3>');
+    buffer.writeln('        <p>All tests comply with WCAG standards.</p>');
     buffer.writeln('      </div>');
   } else {
     for (var i = 0; i < overallAnalysis.insights.length; i++) {
@@ -276,7 +279,7 @@
 
   buffer.writeln('    </section>');
 
-  // === 各 SC 詳細報告 ===
+  // === Individual SC Reports ===
   for (final report in batch.reports) {
     final scId =
         report.successCriterion.replaceAll(' ', '_').replaceAll('.', '_');
@@ -284,7 +287,7 @@
 
     buffer.writeln('    <section id="$scId" class="section sc-section">');
 
-    // SC 標題
+    // SC Title
     buffer.writeln('      <div class="sc-header">');
     buffer.writeln('        <div>');
     buffer.writeln(
@@ -293,20 +296,21 @@
     buffer.writeln(
         '            <span class="level-badge level-${report.level.name}">${report.level.label}</span>');
     buffer.writeln(
-        '            <span>Compliance性: ${report.score.statusEmoji} ${report.score.percentage.toStringAsFixed(1)}%</span>');
+        '            <span>Compliance: ${report.score.statusEmoji} ${report.score.percentage.toStringAsFixed(1)}%</span>');
     buffer.writeln(
         '            <span>Health Score: ${_getHealthEmoji(analysis.healthScore)} ${(analysis.healthScore * 100).toStringAsFixed(1)}%</span>');
     buffer.writeln('          </div>');
     buffer.writeln('        </div>');
-    buffer.writeln('        <a href="#overview" class="back-link">↑ 返回總覽</a>');
+    buffer.writeln(
+        '        <a href="#overview" class="back-link">↑ Back to Overview</a>');
     buffer.writeln('      </div>');
 
-    // SC 統計卡片
+    // SC Stats Card
     buffer.writeln('      <div class="sc-stats-grid">');
     buffer.writeln('        <div class="stat-card">');
     buffer.writeln(
         '          <div class="stat-value">${report.score.passed}/${report.score.total}</div>');
-    buffer.writeln('          <div class="stat-label">Passed測試</div>');
+    buffer.writeln('          <div class="stat-label">Passed Tests</div>');
     buffer.writeln('        </div>');
     buffer.writeln(
         '        <div class="stat-card ${report.criticalFailures.isNotEmpty ? 'critical' : ''}">');
@@ -317,19 +321,19 @@
     buffer.writeln('        <div class="stat-card">');
     buffer.writeln(
         '          <div class="stat-value">${analysis.insights.length}</div>');
-    buffer.writeln('          <div class="stat-label">發現洞察</div>');
+    buffer.writeln('          <div class="stat-label">Insights Found</div>');
     buffer.writeln('        </div>');
     buffer.writeln('        <div class="stat-card">');
     buffer.writeln(
         '          <div class="stat-value">${analysis.estimatedEffort?.toStringAsFixed(1) ?? 'N/A'}h</div>');
-    buffer.writeln('          <div class="stat-label">修復工作量</div>');
+    buffer.writeln('          <div class="stat-label">Fix Effort</div>');
     buffer.writeln('        </div>');
     buffer.writeln('      </div>');
 
-    // SC 特定的AI analysis
+    // SC Specific AI Analysis
     if (analysis.insights.isNotEmpty) {
       buffer.writeln('      <div class="sc-analysis">');
-      buffer.writeln('        <h3>🧠 此 SC 的AI analysis</h3>');
+      buffer.writeln('        <h3>🧠 AI Analysis for this SC</h3>');
       for (var i = 0; i < analysis.insights.length; i++) {
         buffer.write(
             _generateInsightCard(analysis.insights[i], i + 1, compact: true));
@@ -337,16 +341,16 @@
       buffer.writeln('      </div>');
     }
 
-    // 測試結果表格
+    // Test Results Table
     if (report.results.isNotEmpty) {
-      buffer.writeln('      <h3>📋 測試結果</h3>');
+      buffer.writeln('      <h3>📋 Test Results</h3>');
       buffer.writeln('      <table class="results-table">');
       buffer.writeln('        <thead>');
       buffer.writeln('          <tr>');
-      buffer.writeln('            <th>元件</th>');
-      buffer.writeln('            <th>狀態</th>');
-      buffer.writeln('            <th>嚴重性</th>');
-      buffer.writeln('            <th>說明</th>');
+      buffer.writeln('            <th>Component</th>');
+      buffer.writeln('            <th>Status</th>');
+      buffer.writeln('            <th>Severity</th>');
+      buffer.writeln('            <th>Description</th>');
       buffer.writeln('          </tr>');
       buffer.writeln('        </thead>');
       buffer.writeln('        <tbody>');
@@ -381,7 +385,7 @@
   return buffer.toString();
 }
 
-/// 生成洞察卡片 HTML
+/// Generate Insight Card HTML
 String _generateInsightCard(Insight insight, int priority,
     {bool compact = false}) {
   final severityClass = 'insight-${insight.severity.name}';
@@ -390,7 +394,7 @@
       <div class="insight-card $severityClass">
         <div class="insight-header">
           <div class="insight-priority">
-            <span class="priority-badge">優先級 $priority</span>
+            <span class="priority-badge">Priority $priority</span>
           </div>
           <div class="insight-title-group">
             <span class="insight-emoji">${insight.severity.emoji}</span>
@@ -413,7 +417,7 @@
             </div>
             <div class="detail-item">
               <span class="detail-icon">🔢</span>
-              <span class="detail-label">Failed次數:</span>
+              <span class="detail-label">Failure Count:</span>
               <span class="detail-value">${insight.failureCount}</span>
             </div>
           </div>
@@ -446,10 +450,10 @@
 ''';
 }
 
-/// 生成圖表 JavaScript
+/// Generate Chart JavaScript
 String _generateChartScript(WcagBatchResult batch) {
   return '''
-    // 整體Compliance Doughnut Chart
+    // Overall Compliance Doughnut Chart
     new Chart(document.getElementById('overallChart'), {
       type: 'doughnut',
       data: {
@@ -475,15 +479,15 @@
       }
     });
 
-    // 各 SC Compliance Rate長條圖
+    // SC Compliance Rate Bar Chart
     new Chart(document.getElementById('complianceChart'), {
       type: 'bar',
       data: {
-        labels: [${batch.reports.map((r) => '"${r.successCriterion}"').join(', ')}],
+        labels: ${jsonEncode(batch.reports.map((r) => r.successCriterion).toList())},
         datasets: [{
           label: 'Compliance Rate (%)',
-          data: [${batch.reports.map((r) => r.score.percentage).join(', ')}],
-          backgroundColor: [${batch.reports.map((r) => _getBarColor(r.score.percentage)).join(', ')}],
+          data: ${jsonEncode(batch.reports.map((r) => r.score.percentage).toList())},
+          backgroundColor: ${jsonEncode(batch.reports.map((r) => _getBarColor(r.score.percentage)).toList())},
           borderWidth: 0
         }]
       },
@@ -511,14 +515,14 @@
   ''';
 }
 
-/// 取得長條圖顏色
+/// Get Bar Color
 String _getBarColor(double percentage) {
   if (percentage >= 95) return "'#28a745'";
   if (percentage >= 80) return "'#ffc107'";
   return "'#dc3545'";
 }
 
-/// 取得Health Score的卡片 class
+/// Get Health Score Card Class
 String _getHealthCardClass(double score) {
   if (score >= 0.8) return 'success';
   if (score >= 0.5) return 'warning';
@@ -546,15 +550,10 @@
 
 /// HTML Escape
 String _escapeHtml(String text) {
-  return text
-      .replaceAll('&', '&amp;')
-      .replaceAll('<', '&lt;')
-      .replaceAll('>', '&gt;')
-      .replaceAll('"', '&quot;')
-      .replaceAll("'", '&#39;');
+  return htmlEscape.convert(text);
 }

Replace the manual HTML escaping function with the more robust and secure
htmlEscape utility from dart:convert to prevent potential XSS vulnerabilities.

test/accessibility/examples/wcag_batch_with_analysis.dart [548-555]

+import 'dart:convert';
+
 /// HTML Escape
 String _escapeHtml(String text) {
-  return text
-      .replaceAll('&', '&amp;')
-      .replaceAll('<', '&lt;')
-      .replaceAll('>', '&gt;')
-      .replaceAll('"', '&quot;')
-      .replaceAll("'", '&#39;');
+  return htmlEscape.convert(text);
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that using a standard library function like htmlEscape is more secure and robust than a manual implementation, which is a critical improvement for preventing XSS vulnerabilities.

Medium
Possible issue
Fix inconsistent chart data scaling
Suggestion Impact:Replaced the simple (bandwidthKbps/1024) mapping with unit-aware logic that uses non-zero bandwidthKbps when available and otherwise parses the speed string and converts from KBPS to Mbps, preventing mixed-scale chart data.

code diff:

-    final downloads = data
-        .map((e) => (e.downloadBandwidthKbps ?? 0) / 1024.0)
-        .toList(); // Mbps
-    final uploads =
-        data.map((e) => (e.uploadBandwidthKbps ?? 0) / 1024.0).toList(); // Mbps
+    final downloads = data.map((e) {
+      if (e.downloadBandwidthKbps != null && e.downloadBandwidthKbps! > 0) {
+        return e.downloadBandwidthKbps! / 1024.0; // Mbps
+      }
+      // Fallback to parsing string value
+      final speed = double.tryParse(e.downloadSpeed) ?? 0;
+      if (e.downloadUnit.toUpperCase() == 'KBPS') {
+        return speed / 1024.0;
+      }
+      return speed; // Assume Mbps
+    }).toList();
+
+    final uploads = data.map((e) {
+      if (e.uploadBandwidthKbps != null && e.uploadBandwidthKbps! > 0) {
+        return e.uploadBandwidthKbps! / 1024.0; // Mbps
+      }
+      // Fallback to parsing string value
+      final speed = double.tryParse(e.uploadSpeed) ?? 0;
+      if (e.uploadUnit.toUpperCase() == 'KBPS') {
+        return speed / 1024.0;
+      }
+      return speed; // Assume Mbps
+    }).toList();

Fix inconsistent data scaling in the history chart by ensuring all speed values
are correctly converted to Mbps before being rendered.

lib/page/dashboard/views/components/widgets/atomic/speed_test.dart [398-412]

-final downloads = data
-    .map((e) => (e.downloadBandwidthKbps ?? 0) / 1024.0)
-    .toList(); // Mbps
-final uploads =
-    data.map((e) => (e.uploadBandwidthKbps ?? 0) / 1024.0).toList(); // Mbps
+final downloads = data.map((e) {
+  if (e.downloadBandwidthKbps != null && e.downloadBandwidthKbps! > 0) {
+    return e.downloadBandwidthKbps! / 1024.0; // Mbps
+  }
+  // Fallback to parsing string value
+  final speed = double.tryParse(e.downloadSpeed) ?? 0;
+  if (e.downloadUnit.toUpperCase() == 'KBPS') {
+    return speed / 1024.0;
+  }
+  return speed; // Assume Mbps
+}).toList();
 
-// If kbps is null/0, try parsing string (fallback)
-for (int i = 0; i < data.length; i++) {
-  if (downloads[i] == 0) {
-    downloads[i] = double.tryParse(data[i].downloadSpeed) ?? 0;
+final uploads = data.map((e) {
+  if (e.uploadBandwidthKbps != null && e.uploadBandwidthKbps! > 0) {
+    return e.uploadBandwidthKbps! / 1024.0; // Mbps
   }
-  if (uploads[i] == 0) {
-    uploads[i] = double.tryParse(data[i].uploadSpeed) ?? 0;
+  // Fallback to parsing string value
+  final speed = double.tryParse(e.uploadSpeed) ?? 0;
+  if (e.uploadUnit.toUpperCase() == 'KBPS') {
+    return speed / 1024.0;
   }
-}
+  return speed; // Assume Mbps
+}).toList();

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: This suggestion fixes a critical data normalization bug where speed values with different units (Kbps and Mbps) could be plotted on the same chart axis, leading to incorrect visualizations.

Medium
Prevent crash by checking for empty list
Suggestion Impact:The code now stores the watched topology in a variable, checks for an empty children list, returns SizedBox.shrink() if empty, and only then accesses children.first.

code diff:

-    final master = ref.watch(instantTopologyProvider).root.children.first;
+    final topology = ref.watch(instantTopologyProvider);
+    if (topology.root.children.isEmpty) {
+      return const SizedBox.shrink();
+    }
+    final master = topology.root.children.first;

Add a guard to check if topology.root.children is empty before accessing its
first element to prevent a potential crash.

lib/page/dashboard/views/components/widgets/atomic/master_node_info.dart [35-40]

 @override
 Widget buildCompactView(BuildContext context, WidgetRef ref) {
   // Compact: Just image and location/name centered
-  final master = ref.watch(instantTopologyProvider).root.children.first;
+  final topology = ref.watch(instantTopologyProvider);
+  if (topology.root.children.isEmpty) {
+    return const SizedBox.shrink();
+  }
+  final master = topology.root.children.first;
   final masterIcon = ref.watch(dashboardHomeProvider).masterIcon;
 
   return AppInkWell(
 ...

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: This suggestion prevents a potential RangeError crash by adding a necessary guard clause, which is a pattern already used in other parts of the PR for robustness.

Medium
Always stop refresh animation
Suggestion Impact:Updated `forcePolling()` callbacks from `.then(...)` to `.whenComplete(...)` (in multiple places) to ensure `controller.stop()` always runs.

code diff:

                 controller.repeat();
-                ref.read(pollingProvider.notifier).forcePolling().then((_) {
+                ref
+                    .read(pollingProvider.notifier)
+                    .forcePolling()
+                    .whenComplete(() {
                   controller.stop();
                 });
               },
@@ -186,7 +189,7 @@
                           ref
                               .read(pollingProvider.notifier)
                               .forcePolling()
-                              .then((value) {
+                              .whenComplete(() {
                             controller.stop();
                           });
                         },
@@ -370,7 +373,7 @@
                           ref
                               .read(pollingProvider.notifier)
                               .forcePolling()
-                              .then((_) {
+                              .whenComplete(() {
                             controller.stop();
                           });

Use whenComplete instead of then to ensure the refresh animation controller is
always stopped, even if the forcePolling future completes with an error.

lib/page/dashboard/views/components/fixed_layout/internet_status.dart [100-102]

-ref.read(pollingProvider.notifier).forcePolling().then((_) {
-  controller.stop();
-});
+ref.read(pollingProvider.notifier)
+  .forcePolling()
+  .whenComplete(() => controller.stop());

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a potential UI bug where an animation would not stop on an error and proposes using whenComplete for more robust error handling, which is a pattern used elsewhere in the PR.

Medium
General
Serialize JS arrays with jsonEncode
Suggestion Impact:Added dart:convert and changed Chart.js config to use jsonEncode for labels, data, and backgroundColor arrays instead of manual string interpolation.

code diff:

+import 'dart:convert';
 import 'dart:io';
 import 'package:flutter/material.dart';
 import 'package:ui_kit_library/src/foundation/accessibility/accessibility.dart';
 
-/// 為 WcagBatchResult 生成包含AI analysis的完整 HTML 報告
+/// Generate complete HTML report with AI analysis for WcagBatchResult
 String generateFullHtmlWithAnalysis({
   required WcagBatchResult batch,
   WcagBatchResult? previousBatch,
   bool includeFixSuggestions = true,
 }) {
-  // 執行整體AI analysis(跨所有 SC)
+  // Perform overall AI analysis (across all SCs)
   final engine = WcagAnalysisEngine();
   final overallAnalysis = engine.analyzeMultiple(
     batch.reports,
     includeFixSuggestions: includeFixSuggestions,
   );
 
-  // 為每個 SC 執行個別分析
+  // Perform individual analysis for each SC
   final individualAnalyses = <String, AnalysisResult>{};
   for (final report in batch.reports) {
     WcagReport? previousReport;
     if (previousBatch != null) {
-      // 找出對應的前一版本報告
+      // Find corresponding previous version report
       final reportType = report.successCriterion;
       previousReport = previousBatch.reports.cast<WcagReport?>().firstWhere(
             (r) => r?.successCriterion == reportType,
@@ -46,13 +47,13 @@
   final buffer = StringBuffer();
 
   buffer.writeln('<!DOCTYPE html>');
-  buffer.writeln('<html lang="zh-TW">');
+  buffer.writeln('<html lang="en">');
   buffer.writeln('<head>');
   buffer.writeln('  <meta charset="UTF-8">');
   buffer.writeln(
       '  <meta name="viewport" content="width=device-width, initial-scale=1.0">');
   buffer.writeln(
-      '  <title>WCAG 完整Compliance報告(含AI analysis)- v${batch.metadata.version}</title>');
+      '  <title>WCAG Complete Compliance Report (with AI Analysis) - v${batch.metadata.version}</title>');
   buffer.writeln(
       '  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>');
   buffer.writeln('  <style>');
@@ -61,18 +62,18 @@
   buffer.writeln('</head>');
   buffer.writeln('<body>');
 
-  // === 頁首導航 ===
+  // === Header Navigation ===
   buffer.writeln('  <div class="header">');
   buffer.writeln('    <div class="container">');
   buffer.writeln(
-      '      <h1>${batch.statusEmoji} WCAG 完整Compliance報告(含AI analysis)</h1>');
+      '      <h1>${batch.statusEmoji} WCAG Complete Compliance Report (with AI Analysis)</h1>');
   buffer.writeln('      <div class="header-subtitle">');
   buffer.writeln(
       '        Version ${batch.metadata.version} • ${batch.metadata.environment} • ${batch.metadata.timestamp.toString().substring(0, 19)}');
   buffer.writeln('      </div>');
   buffer.writeln('      <div class="nav-links">');
-  buffer.writeln('        <a href="#overview">📊 總覽</a>');
-  buffer.writeln('        <a href="#analysis">🧠 AI analysis</a>');
+  buffer.writeln('        <a href="#overview">📊 Overview</a>');
+  buffer.writeln('        <a href="#analysis">🧠 AI Analysis</a>');
   for (final report in batch.reports) {
     final scId =
         report.successCriterion.replaceAll(' ', '_').replaceAll('.', '_');
@@ -84,18 +85,19 @@
 
   buffer.writeln('  <div class="container">');
 
-  // === 總覽區塊 ===
+  // === Overview Section ===
   buffer.writeln('    <section id="overview" class="section">');
-  buffer.writeln('      <h2>📊 整體總覽</h2>');
-
-  // 關鍵指標卡片
+  buffer.writeln('      <h2>📊 Overall Overview</h2>');
+
+  // Key Metrics Cards
   buffer.writeln('      <div class="metrics-grid">');
 
   buffer.writeln('        <div class="metric-card">');
   buffer.writeln('          <div class="metric-icon">📈</div>');
   buffer.writeln(
       '          <div class="metric-value">${batch.overallCompliance.toStringAsFixed(1)}%</div>');
-  buffer.writeln('          <div class="metric-label">整體Compliance性</div>');
+  buffer
+      .writeln('          <div class="metric-label">Overall Compliance</div>');
   buffer.writeln('        </div>');
 
   buffer.writeln(
@@ -140,9 +142,9 @@
 
   buffer.writeln('      </div>');
 
-  // 元數據資訊
+  // Metadata Info
   buffer.writeln('      <div class="metadata-card">');
-  buffer.writeln('        <h3>報告資訊</h3>');
+  buffer.writeln('        <h3>Report Information</h3>');
   buffer.writeln('        <div class="metadata-grid">');
   buffer.writeln(
       '          <div><strong>Git Commit:</strong> ${batch.metadata.gitCommitHash}</div>');
@@ -159,34 +161,34 @@
   buffer.writeln('        </div>');
   buffer.writeln('      </div>');
 
-  // 圖表區域
+  // Charts Area
   buffer.writeln('      <div class="charts-row">');
   buffer.writeln('        <div class="chart-card">');
-  buffer.writeln('          <h3>Compliance性分布</h3>');
+  buffer.writeln('          <h3>Compliance Distribution</h3>');
   buffer.writeln('          <div class="chart-container">');
   buffer.writeln('            <canvas id="overallChart"></canvas>');
   buffer.writeln('          </div>');
   buffer.writeln('        </div>');
   buffer.writeln('        <div class="chart-card">');
-  buffer.writeln('          <h3>各 SC Compliance Rate</h3>');
+  buffer.writeln('          <h3>SC Compliance Rate</h3>');
   buffer.writeln('          <div class="chart-container">');
   buffer.writeln('            <canvas id="complianceChart"></canvas>');
   buffer.writeln('          </div>');
   buffer.writeln('        </div>');
   buffer.writeln('      </div>');
 
-  // SC 總覽表格
-  buffer.writeln('      <h3>Success Criteria 詳情</h3>');
+  // SC Overview Table
+  buffer.writeln('      <h3>Success Criteria Details</h3>');
   buffer.writeln('      <table class="sc-table">');
   buffer.writeln('        <thead>');
   buffer.writeln('          <tr>');
   buffer.writeln('            <th>SC</th>');
-  buffer.writeln('            <th>標題</th>');
-  buffer.writeln('            <th>等級</th>');
-  buffer.writeln('            <th>Compliance性</th>');
+  buffer.writeln('            <th>Title</th>');
+  buffer.writeln('            <th>Level</th>');
+  buffer.writeln('            <th>Compliance</th>');
   buffer.writeln('            <th>Health Score</th>');
-  buffer.writeln('            <th>關鍵問題</th>');
-  buffer.writeln('            <th>操作</th>');
+  buffer.writeln('            <th>Critical Issues</th>');
+  buffer.writeln('            <th>Action</th>');
   buffer.writeln('          </tr>');
   buffer.writeln('        </thead>');
   buffer.writeln('        <tbody>');
@@ -197,7 +199,7 @@
     buffer.writeln('          <tr>');
     buffer.writeln(
         '            <td><strong>${report.successCriterion}</strong></td>');
-    buffer.writeln('            <td>${report.title}</td>');
+    buffer.writeln('            <td>${_escapeHtml(report.title)}</td>');
     buffer.writeln(
         '            <td><span class="level-badge level-${report.level.name}">${report.level.label}</span></td>');
     buffer.writeln(
@@ -207,7 +209,7 @@
     buffer.writeln(
         '            <td>${analysis.criticalInsights.length > 0 ? '🔴 ${analysis.criticalInsights.length}' : '✅'}</td>');
     buffer.writeln(
-        '            <td><a href="#$scId" class="btn-link">查看詳情 →</a></td>');
+        '            <td><a href="#$scId" class="btn-link">View Details →</a></td>');
     buffer.writeln('          </tr>');
   }
   buffer.writeln('        </tbody>');
@@ -215,10 +217,10 @@
 
   buffer.writeln('    </section>');
 
-  // === AI analysis區塊 ===
+  // === AI Analysis Section ===
   buffer
       .writeln('    <section id="analysis" class="section analysis-section">');
-  buffer.writeln('      <h2>🧠 AI analysis:整體洞察</h2>');
+  buffer.writeln('      <h2>🧠 AI Analysis: Overall Insights</h2>');
 
   // Regression Warning
   if (overallAnalysis.regressions.isNotEmpty) {
@@ -231,7 +233,7 @@
       buffer.writeln('          <li>');
       buffer.writeln('            <strong>${regression.title}</strong><br>');
       buffer.writeln(
-          '            <span class="text-muted">受影響:${regression.affectedComponents.join(", ")}</span>');
+          '            <span class="text-muted">Affected: ${regression.affectedComponents.join(", ")}</span>');
       buffer.writeln('          </li>');
     }
     buffer.writeln('        </ul>');
@@ -241,8 +243,9 @@
   // Systemic
   if (overallAnalysis.systemicIssues.isNotEmpty) {
     buffer.writeln('      <div class="alert alert-warning">');
-    buffer.writeln('        <h3>⚠️ Systemic</h3>');
-    buffer.writeln('        <p>以下元件在多個 Success Criteria 或情境中Failed:</p>');
+    buffer.writeln('        <h3>⚠️ Systemic Issues Detected!</h3>');
+    buffer.writeln(
+        '        <p>The following components failed across multiple Success Criteria or scenarios:</p>');
     buffer.writeln('        <ul>');
     for (final systemic in overallAnalysis.systemicIssues) {
       buffer.writeln('          <li>');
@@ -256,16 +259,16 @@
     buffer.writeln('      </div>');
   }
 
-  // priority sorting的洞察
-  buffer.writeln('      <h3>💡 優先修復順序(跨所有 SC)</h3>');
-  buffer.writeln(
-      '      <p class="section-subtitle">根據Severity、影響範圍和 WCAG 等級自動排序</p>');
+  // Priority Sorting Insights
+  buffer.writeln('      <h3>💡 Priority Fix Order (Across All SCs)</h3>');
+  buffer.writeln(
+      '      <p class="section-subtitle">Automatically sorted by Severity, Scope, and WCAG Level</p>');
 
   if (overallAnalysis.insights.isEmpty) {
     buffer.writeln('      <div class="success-message">');
     buffer.writeln('        <div style="font-size: 64px;">✅</div>');
-    buffer.writeln('        <h3>沒有發現問題模式!</h3>');
-    buffer.writeln('        <p>All tests comply with WCAG 標準。</p>');
+    buffer.writeln('        <h3>No problem patterns detected!</h3>');
+    buffer.writeln('        <p>All tests comply with WCAG standards.</p>');
     buffer.writeln('      </div>');
   } else {
     for (var i = 0; i < overallAnalysis.insights.length; i++) {
@@ -276,7 +279,7 @@
 
   buffer.writeln('    </section>');
 
-  // === 各 SC 詳細報告 ===
+  // === Individual SC Reports ===
   for (final report in batch.reports) {
     final scId =
         report.successCriterion.replaceAll(' ', '_').replaceAll('.', '_');
@@ -284,7 +287,7 @@
 
     buffer.writeln('    <section id="$scId" class="section sc-section">');
 
-    // SC 標題
+    // SC Title
     buffer.writeln('      <div class="sc-header">');
     buffer.writeln('        <div>');
     buffer.writeln(
@@ -293,20 +296,21 @@
     buffer.writeln(
         '            <span class="level-badge level-${report.level.name}">${report.level.label}</span>');
     buffer.writeln(
-        '            <span>Compliance性: ${report.score.statusEmoji} ${report.score.percentage.toStringAsFixed(1)}%</span>');
+        '            <span>Compliance: ${report.score.statusEmoji} ${report.score.percentage.toStringAsFixed(1)}%</span>');
     buffer.writeln(
         '            <span>Health Score: ${_getHealthEmoji(analysis.healthScore)} ${(analysis.healthScore * 100).toStringAsFixed(1)}%</span>');
     buffer.writeln('          </div>');
     buffer.writeln('        </div>');
-    buffer.writeln('        <a href="#overview" class="back-link">↑ 返回總覽</a>');
+    buffer.writeln(
+        '        <a href="#overview" class="back-link">↑ Back to Overview</a>');
     buffer.writeln('      </div>');
 
-    // SC 統計卡片
+    // SC Stats Card
     buffer.writeln('      <div class="sc-stats-grid">');
     buffer.writeln('        <div class="stat-card">');
     buffer.writeln(
         '          <div class="stat-value">${report.score.passed}/${report.score.total}</div>');
-    buffer.writeln('          <div class="stat-label">Passed測試</div>');
+    buffer.writeln('          <div class="stat-label">Passed Tests</div>');
     buffer.writeln('        </div>');
     buffer.writeln(
         '        <div class="stat-card ${report.criticalFailures.isNotEmpty ? 'critical' : ''}">');
@@ -317,19 +321,19 @@
     buffer.writeln('        <div class="stat-card">');
     buffer.writeln(
         '          <div class="stat-value">${analysis.insights.length}</div>');
-    buffer.writeln('          <div class="stat-label">發現洞察</div>');
+    buffer.writeln('          <div class="stat-label">Insights Found</div>');
     buffer.writeln('        </div>');
     buffer.writeln('        <div class="stat-card">');
     buffer.writeln(
         '          <div class="stat-value">${analysis.estimatedEffort?.toStringAsFixed(1) ?? 'N/A'}h</div>');
-    buffer.writeln('          <div class="stat-label">修復工作量</div>');
+    buffer.writeln('          <div class="stat-label">Fix Effort</div>');
     buffer.writeln('        </div>');
     buffer.writeln('      </div>');
 
-    // SC 特定的AI analysis
+    // SC Specific AI Analysis
     if (analysis.insights.isNotEmpty) {
       buffer.writeln('      <div class="sc-analysis">');
-      buffer.writeln('        <h3>🧠 此 SC 的AI analysis</h3>');
+      buffer.writeln('        <h3>🧠 AI Analysis for this SC</h3>');
       for (var i = 0; i < analysis.insights.length; i++) {
         buffer.write(
             _generateInsightCard(analysis.insights[i], i + 1, compact: true));
@@ -337,16 +341,16 @@
       buffer.writeln('      </div>');
     }
 
-    // 測試結果表格
+    // Test Results Table
     if (report.results.isNotEmpty) {
-      buffer.writeln('      <h3>📋 測試結果</h3>');
+      buffer.writeln('      <h3>📋 Test Results</h3>');
       buffer.writeln('      <table class="results-table">');
       buffer.writeln('        <thead>');
       buffer.writeln('          <tr>');
-      buffer.writeln('            <th>元件</th>');
-      buffer.writeln('            <th>狀態</th>');
-      buffer.writeln('            <th>嚴重性</th>');
-      buffer.writeln('            <th>說明</th>');
+      buffer.writeln('            <th>Component</th>');
+      buffer.writeln('            <th>Status</th>');
+      buffer.writeln('            <th>Severity</th>');
+      buffer.writeln('            <th>Description</th>');
       buffer.writeln('          </tr>');
       buffer.writeln('        </thead>');
       buffer.writeln('        <tbody>');
@@ -381,7 +385,7 @@
   return buffer.toString();
 }
 
-/// 生成洞察卡片 HTML
+/// Generate Insight Card HTML
 String _generateInsightCard(Insight insight, int priority,
     {bool compact = false}) {
   final severityClass = 'insight-${insight.severity.name}';
@@ -390,7 +394,7 @@
       <div class="insight-card $severityClass">
         <div class="insight-header">
           <div class="insight-priority">
-            <span class="priority-badge">優先級 $priority</span>
+            <span class="priority-badge">Priority $priority</span>
           </div>
           <div class="insight-title-group">
             <span class="insight-emoji">${insight.severity.emoji}</span>
@@ -413,7 +417,7 @@
             </div>
             <div class="detail-item">
               <span class="detail-icon">🔢</span>
-              <span class="detail-label">Failed次數:</span>
+              <span class="detail-label">Failure Count:</span>
               <span class="detail-value">${insight.failureCount}</span>
             </div>
           </div>
@@ -446,10 +450,10 @@
 ''';
 }
 
-/// 生成圖表 JavaScript
+/// Generate Chart JavaScript
 String _generateChartScript(WcagBatchResult batch) {
   return '''
-    // 整體Compliance Doughnut Chart
+    // Overall Compliance Doughnut Chart
     new Chart(document.getElementById('overallChart'), {
       type: 'doughnut',
       data: {
@@ -475,15 +479,15 @@
       }
     });
 
-    // 各 SC Compliance Rate長條圖
+    // SC Compliance Rate Bar Chart
     new Chart(document.getElementById('complianceChart'), {
       type: 'bar',
       data: {
-        labels: [${batch.reports.map((r) => '"${r.successCriterion}"').join(', ')}],
+        labels: ${jsonEncode(batch.reports.map((r) => r.successCriterion).toList())},
         datasets: [{
           label: 'Compliance Rate (%)',
-          data: [${batch.reports.map((r) => r.score.percentage).join(', ')}],
-          backgroundColor: [${batch.reports.map((r) => _getBarColor(r.score.percentage)).join(', ')}],
+          data: ${jsonEncode(batch.reports.map((r) => r.score.percentage).toList())},
+          backgroundColor: ${jsonEncode(batch.reports.map((r) => _getBarColor(r.score.percentage)).toList())},
           borderWidth: 0
         }]

Use jsonEncode to safely serialize data arrays for JavaScript, preventing syntax
errors and injection issues from special characters in labels or values.

test/accessibility/examples/wcag_batch_with_analysis.dart [479-489]

 new Chart(document.getElementById('complianceChart'), {
   type: 'bar',
   data: {
-    labels: [${batch.reports.map((r) => '"${r.successCriterion}"').join(', ')}],
+    labels: ${jsonEncode(batch.reports.map((r) => r.successCriterion).toList())},
     datasets: [{
       label: 'Compliance Rate (%)',
-      data: [${batch.reports.map((r) => r.score.percentage).join(', ')}],
-      backgroundColor: [${batch.reports.map((r) => _getBarColor(r.score.percentage)).join(', ')}],
+      data: ${jsonEncode(batch.reports.map((r) => r.score.percentage).toList())},
+      backgroundColor: ${jsonEncode(batch.reports.map((r) => _getBarColor(r.score.percentage)).toList())},
       borderWidth: 0
     }]
   },
   ...
 });

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that using jsonEncode is a safer way to serialize data for JavaScript, preventing potential syntax errors and injection issues.

Medium
Update HTML language attribute
Suggestion Impact:The commit changes the HTML root tag language attribute from "zh-TW" to "en" as suggested.

code diff:

-<html lang="zh-TW">
+<html lang="en">

Update the HTML lang attribute from zh-TW to en to match the English content of
the report.

test/accessibility/examples/wcag_html_with_analysis_example.dart [29]

-<html lang="zh-TW">
+<html lang="en">

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that the HTML lang attribute is set to zh-TW for an English report, which is semantically incorrect and particularly important to fix in an accessibility-focused example.

Low
Use public accessibility import

Change the import from an internal src path to the public API path to avoid
potential breaking changes in the future.

test/accessibility/example_validation.dart [14]

-import 'package:ui_kit_library/src/foundation/accessibility/accessibility.dart';
+import 'package:ui_kit_library/foundation/accessibility/accessibility.dart';

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies the use of an internal package import (src folder), which is a bad practice, and recommends using the public API to improve maintainability and prevent future breakage.

Low
Translate hardcoded strings to English
Suggestion Impact:

- Refactored  to hide raw error details from UI (Generic: Robust Error Handling)
- Updated  to escape HTML in dynamic fields (Generic: XSS Prevention)
- Addressed const lint error
@PeterJhongLinksys PeterJhongLinksys merged commit f082bb9 into dev-2.0.0 Feb 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants