Skip to content

Commit 6437c67

Browse files
Merge pull request #7 from brewkits/feat/coverage-improvements
feat: enhance test coverage and fix layout warnings
2 parents c49aa0a + 1c8cb4e commit 6437c67

164 files changed

Lines changed: 5817 additions & 808 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Plugin Request
2+
description: Request a new HyperRender plugin (e.g. math, charts, diagrams)
3+
title: "[Plugin Request] <plugin name>"
4+
labels: ["plugin", "enhancement"]
5+
body:
6+
- type: markdown
7+
attributes:
8+
value: |
9+
Thanks for suggesting a plugin! Community plugins extend HyperRender without touching the core engine.
10+
Read [PLUGIN_DEVELOPMENT.md](../blob/main/doc/PLUGIN_DEVELOPMENT.md) before submitting to understand what's buildable.
11+
12+
- type: input
13+
id: tag_name
14+
attributes:
15+
label: HTML tag(s) to handle
16+
description: Which custom or standard HTML tag(s) should this plugin render?
17+
placeholder: "<math>, <latex>, <chart>, <mermaid>, ..."
18+
validations:
19+
required: true
20+
21+
- type: textarea
22+
id: use_case
23+
attributes:
24+
label: Use case
25+
description: What content will this plugin render? Where is it used? (CMS, e-book, documentation, etc.)
26+
placeholder: "Our CMS exports math equations as <latex> tags inline with article text. We need them rendered as readable formulas on mobile."
27+
validations:
28+
required: true
29+
30+
- type: dropdown
31+
id: tier
32+
attributes:
33+
label: Plugin tier
34+
description: Block plugins take full width (like images). Inline plugins flow inside text lines (like icons).
35+
options:
36+
- Block (full width, like a figure or table)
37+
- Inline (flows with text, like an icon or badge)
38+
- Not sure
39+
validations:
40+
required: true
41+
42+
- type: textarea
43+
id: proposed_package
44+
attributes:
45+
label: Rendering library (optional)
46+
description: Any Flutter package you'd suggest for the actual rendering?
47+
placeholder: "flutter_math_fork for LaTeX, fl_chart for charts, flutter_svg for SVGs, ..."
48+
49+
- type: checkboxes
50+
id: willing_to_build
51+
attributes:
52+
label: Are you willing to build this?
53+
options:
54+
- label: "Yes — I can submit a PR (see PLUGIN_DEVELOPMENT.md for the guide)"
55+
- label: "No — I'm requesting someone else builds it"
56+
- label: "Partial — I can help review or test but not lead the implementation"
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Plugin Submission
2+
description: Submit a community plugin you've built for HyperRender
3+
title: "[Plugin Submission] hyper_render_<name>"
4+
labels: ["plugin", "community"]
5+
body:
6+
- type: markdown
7+
attributes:
8+
value: |
9+
Thanks for building a HyperRender plugin!
10+
Please ensure your plugin meets the requirements in [PLUGIN_DEVELOPMENT.md — PR Requirements](../blob/main/doc/PLUGIN_DEVELOPMENT.md#pr-requirements-for-plugin-prs).
11+
12+
- type: input
13+
id: package_name
14+
attributes:
15+
label: pub.dev package name
16+
description: The package name you plan to publish (follow the `hyper_render_*` naming convention)
17+
placeholder: hyper_render_math
18+
validations:
19+
required: true
20+
21+
- type: input
22+
id: pub_url
23+
attributes:
24+
label: pub.dev URL or GitHub repo
25+
placeholder: "https://github.com/yourname/hyper_render_math"
26+
validations:
27+
required: true
28+
29+
- type: input
30+
id: tag_name
31+
attributes:
32+
label: HTML tag(s) handled
33+
placeholder: "<math>, <latex>"
34+
validations:
35+
required: true
36+
37+
- type: dropdown
38+
id: tier
39+
attributes:
40+
label: Plugin tier
41+
options:
42+
- Block (isInline == false)
43+
- Inline (isInline == true)
44+
- Both
45+
validations:
46+
required: true
47+
48+
- type: textarea
49+
id: description
50+
attributes:
51+
label: What does it render?
52+
description: One paragraph description for the plugin listing.
53+
validations:
54+
required: true
55+
56+
- type: textarea
57+
id: dependencies
58+
attributes:
59+
label: External dependencies
60+
description: List any Flutter packages your plugin depends on (so users know the transitive deps).
61+
placeholder: "flutter_math_fork ^0.7.2"
62+
63+
- type: checkboxes
64+
id: checklist
65+
attributes:
66+
label: Submission checklist
67+
description: All items must be checked before the plugin can be listed.
68+
options:
69+
- label: "Package implements `HyperNodePlugin` from `hyper_render_core`"
70+
required: true
71+
- label: "`build()` returns `null` for unrecognized nodes (safe fallthrough)"
72+
required: true
73+
- label: "At least one widget test covering the happy path"
74+
required: true
75+
- label: "README includes a working code example"
76+
required: true
77+
- label: "CHANGELOG has an initial entry"
78+
required: true
79+
- label: "pubspec.yaml specifies `hyper_render_core: ^1.2.0` or later"
80+
required: true
81+
- label: "Tested on at least one mobile platform (iOS or Android)"
82+
required: true

.github/workflows/analyze.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ jobs:
114114
- name: flutter analyze --fatal-infos
115115
if: steps.filter.outputs.any_dart == 'true'
116116
run: |
117-
flutter analyze --no-pub --fatal-infos 2>&1 | tee analyze_report.txt
117+
flutter analyze --no-pub --fatal-warnings --fatal-infos 2>&1 | tee analyze_report.txt
118118
EXIT=${PIPESTATUS[0]}
119119
120120
ERRORS=$(grep -c "error •" analyze_report.txt 2>/dev/null || true)

.github/workflows/test.yml

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,73 @@ jobs:
7272
- '**/analysis_options.yaml'
7373
7474
# ── PR: fast single-OS run — only changed packages ─────────────────────────
75+
android-compile:
76+
name: Compile (Android)
77+
runs-on: ubuntu-22.04
78+
needs: path-filter
79+
if: >-
80+
github.event_name == 'pull_request' &&
81+
needs.path-filter.outputs.changed_any_dart == 'true'
82+
83+
steps:
84+
- uses: actions/checkout@v4
85+
- name: Setup Java
86+
uses: actions/setup-java@v3
87+
with:
88+
distribution: 'zulu'
89+
java-version: '17'
90+
- name: Setup Flutter
91+
uses: subosito/flutter-action@v2
92+
with:
93+
flutter-version: ${{ env.FLUTTER_VERSION }}
94+
channel: stable
95+
cache: true
96+
- name: flutter pub get
97+
run: flutter pub get
98+
- name: Check Android compileSdk
99+
working-directory: example/android
100+
run: ./gradlew assembleDebug
101+
102+
emulator-tests:
103+
name: Integration Tests (Emulator)
104+
runs-on: macos-latest
105+
needs: path-filter
106+
if: >-
107+
github.event_name == 'pull_request' &&
108+
needs.path-filter.outputs.changed_any_dart == 'true'
109+
strategy:
110+
fail-fast: false
111+
matrix:
112+
platform: [android, ios]
113+
steps:
114+
- uses: actions/checkout@v4
115+
- name: Setup Java
116+
if: matrix.platform == 'android'
117+
uses: actions/setup-java@v3
118+
with:
119+
distribution: 'zulu'
120+
java-version: '17'
121+
- name: Setup Flutter
122+
uses: subosito/flutter-action@v2
123+
with:
124+
flutter-version: ${{ env.FLUTTER_VERSION }}
125+
channel: stable
126+
cache: true
127+
- name: flutter pub get
128+
run: flutter pub get
129+
- name: Run Android Emulator Tests
130+
if: matrix.platform == 'android'
131+
uses: reactivecircus/android-emulator-runner@v2
132+
with:
133+
api-level: 29
134+
arch: arm64-v8a
135+
script: flutter test test/integration/
136+
- name: Run iOS Simulator Tests
137+
if: matrix.platform == 'ios'
138+
run: |
139+
xcrun simctl list devicetypes
140+
flutter test test/integration/ -d "iPhone 15" || flutter test test/integration/
141+
75142
test-pr:
76143
name: Tests (PR · ubuntu-22.04 · stable)
77144
runs-on: ubuntu-22.04
@@ -108,7 +175,10 @@ jobs:
108175
if: >-
109176
needs.path-filter.outputs.changed_root == 'true' ||
110177
needs.path-filter.outputs.changed_core == 'true'
111-
run: flutter test --no-pub
178+
run: |
179+
# Run root tests, excluding test/golden/ which is handled by golden.yml
180+
find test -maxdepth 1 -name "*_test.dart" | xargs flutter test --no-pub
181+
find test -maxdepth 1 -type d -not -path test -not -path test/golden -not -path "test/failures" -not -path "test/.*" | xargs flutter test --no-pub
112182
113183
- name: Test hyper_render_core
114184
if: needs.path-filter.outputs.changed_core == 'true'

.gitignore

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
1-
# Claude Code session files
1+
# AI / LLM Tools
22
.claude/
3+
.gemini/
4+
.cursor/
5+
.windsurf/
6+
.aider*
7+
.env
8+
.env.*
9+
*.local
10+
memory/
11+
RESEARCH.md
12+
PLAN.md
13+
TODO.md
14+
ACT.md
15+
TEST.md
316

417
# Private / internal documents (not for public repository)
518
doc/internal/
@@ -13,17 +26,20 @@ TEST_SUMMARY.md
1326
IMPROVEMENTS_SUMMARY.md
1427
PRIORITY_ACTION_PLAN.md
1528

16-
# Miscellaneous
17-
*.class
18-
*.log
19-
*.pyc
20-
*.swp
29+
# User-specific / Local config
30+
.vscode/
31+
.history/
2132
.DS_Store
22-
.atom/
23-
.buildlog/
24-
.history
25-
.svn/
26-
migrate_working_dir/
33+
*.swp
34+
*.temp
35+
*.tmp
36+
*.log
37+
.DS_Store?
38+
Icon?
39+
ehthumbs.db
40+
Thumbs.db
41+
42+
2743

2844
# IntelliJ related
2945
*.iml

.pubignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ pubspec_dev.yaml
5151
# Internal archive / historical comparison docs
5252
archive/
5353

54+
# AI assistant config files
55+
CLAUDE.md
56+
GEMINI.md
57+
.claude/
58+
5459
# Coverage artifacts
5560
coverage/
5661

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
# Changelog
22

3+
## [1.2.4] - 2026-05-03
4+
5+
### 🐛 Bug Fixes
6+
7+
- **Markdown CRLF line endings left stray `\r` in code blocks** (`markdown_adapter.dart`): `content.split('\n')` on Windows `\r\n` content produced lines like `"some code\r"` — the carriage-return was stored verbatim in code block text and rendered as a stray character in monospace. Content is now normalised to LF (bare `\r` old-Mac endings also handled) before splitting.
8+
- **Virtualized Markdown/Delta sections orphaned headings** (`hyper_viewer.dart`): `_splitIntoSections` (Markdown/Quill Delta virtualized/paged path) lacked the heading-widow guard that `HtmlAdapter.parseToSections` already had. A heading that pushed `currentSize ≥ chunkSize` became the last element of a section, stranding its content at the top of the next section without a heading. Added twin guards: (1) never end a section on `h1``h6`, (2) never split immediately before a heading.
9+
- **`Paint()` allocated every frame for filter/backdrop-filter decorations** (`render_hyper_box_paint.dart`): `_paintBlockDecorations` / `_paintInlineDecorations` created inline `Paint()` objects for CSS `filter` and `backdrop-filter` on every paint call. Added reusable `_filterPaint` instance field (same pattern as `_fillPaint` / `_strokePaint`).
10+
- **`_effectiveConfig` dropped `useMicrotaskParsing` and blocked `allowedCustomSchemes`** (`hyper_viewer.dart`): When HTML contained CSS animations, `_effectiveConfig` rebuilt `HyperRenderConfig` but omitted `useMicrotaskParsing` (reset to `false`, breaking widget tests for animated HTML) and did not merge `allowedCustomSchemes` into `extraLinkSchemes` (custom-scheme deep-links silently blocked at the render layer). Both fields now correctly propagated.
11+
- **`allowedTags` diff used reference equality** (`hyper_viewer.dart`): `didUpdateWidget` compared `oldWidget.allowedTags != widget.allowedTags` which is always `true` for new list literals — causing unnecessary re-parses on every rebuild. Now uses `listEquals` for content equality.
12+
- **Incremental layout hash collision on duplicate sections** (`hyper_viewer.dart`): `_mergeSections` used `Map<int, DocumentNode>` keyed by 32-bit hash; two sections with identical text share the same hash, so the second overwrote the first and both received the same cached node. Changed to `Map<int, List<DocumentNode>>` with queue semantics.
13+
- **`sanitize` doc-comment stated wrong default** (`hyper_viewer.dart`): Comment claimed default was `false`; actual default is `true`. Corrected with accurate security guidance.
14+
- **`addPostFrameCallback` fired on every `build()` in paged mode** (`hyper_viewer.dart`): `_buildPagedContent` registered `pageController._onSectionsReady` unconditionally each build. Now guarded by `_lastNotifiedPageCount` — fires only when section count changes.
15+
16+
## [1.2.3] - 2026-04-30
17+
18+
### 🚀 Performance & Stability
19+
20+
- **Test Coverage Optimization**: Increased global test coverage to >75% with new comprehensive suites for parsers, adapters, and selection logic.
21+
- **Golden Test Alignment**: Updated golden tests for consistent multi-platform rendering validation.
22+
- **Improved Widget Test Robustness**: Updated `find.byType(HyperRenderWidget)` assertions to handle multiple instances in the tree caused by virtualization and float nesting.
23+
24+
### 🐛 Bug Fixes
25+
26+
- **Fixed `HyperRenderWidget` compilation error**: Resolved a signature mismatch in recursive widget construction where `codeHighlighter` was passed outside of `config` and `pluginRegistry` was missing.
27+
- **Fixed Float Layout logic**: Explicit CSS `width` and `height` properties are now correctly respected for non-image float elements, rather than always falling back to intrinsic text dimensions.
28+
- **Fixed Plugin Propagation**: Ensured `pluginRegistry` is correctly passed to nested renderers, allowing custom tags to work inside floated containers.
29+
- **Missing `foundation` import** in `hyper_viewer.dart`: Fixed compilation error when using `compute` function in some environments.
30+
- **Improved selection logic** for virtualized lists: Fixed edge cases when selecting text across off-screen chunks.
31+
- **Flexible Markdown parsing**: Updated adapter to handle variations in tag output (e.g., `<b>` vs `<strong>`) across different environments.
32+
333
## [1.2.2] - 2026-04-02
434

535
### 🐛 Bug Fixes

0 commit comments

Comments
 (0)