Skip to content

Releases: brewkits/hyper_render

v1.3.4

04 Jun 17:58

Choose a tag to compare

🔧 Fixes & Optimizations

  • Static Analysis Compliance: Suppressed deprecated SizeTransition.axisAlignment lints with // ignore: deprecated_member_use to maintain backwards compatibility with older Flutter SDKs (>=3.10) while securing 160/160 points on pub.dev.
  • Dependency Widening: Widened share_plus dependency constraint to ^12.0.2 || ^13.0.0 in hyper_render_clipboard and root packages to allow compatibility with latest stable release.

HyperRender v1.3.3 — object-fit, CSS attribute selectors, memory-pressure callback

04 Jun 17:17

Choose a tag to compare

HyperRender v1.3.3 — Production Readiness Release

A production-readiness release focusing on new CSS properties, rendering correctness, memory management, and publish tooling modernisation. 973 tests passing across all 8 packages.


✨ New CSS Properties

  • object-fit: cover, contain, fill, none, scale-down — applies to <img> elements. Controls how the image content is resized to fit its layout box. Previously, images incorrectly fell through to the background-size mapping; object-fit now takes priority as the semantically correct property for replaced elements.
  • CSS Attribute Selectors: Full support for [attr], [attr="val"], [attr^="val"], [attr$="val"], [attr*="val"], [attr~="val"], [attr|="val"] — enables attribute matching in custom CSS rules and <style> blocks.

✨ New Features

  • onMemoryPressure callback: HyperViewer now exposes an optional VoidCallback? onMemoryPressure parameter (available on all constructors: default, .delta, .markdown, .fromNode). Invoked after HyperRender clears its internal TextPainter, image, and painting caches in response to didHaveMemoryPressure. Enables host apps to release their own resources (video players, download queues, custom caches) in the same memory-pressure cycle.

🐛 Bug Fixes

  • rgb()/rgba() color parsing: csslib's FunctionTerm.span?.text only covers the argument list (e.g. "0, 255, 0)") — the function name is stored separately in FunctionTerm.text ("rgb"). The old inner-iteration code therefore produced "0, 255, 0)" which _parseColor couldn't recognise. Fixed by reconstructing the canonical name(args) form from FunctionTerm.text + span?.text.
  • Ref-counted global TextPainter cache: Multiple HyperViewer instances with different textPainterCacheSize no longer clobber each other's cache size. Each viewer registers its requested size; when a viewer disposes, the largest remaining request is applied. Prevents subtle layout regressions in multi-viewer scenarios.
  • imageConcurrency config wired: HyperRenderConfig.imageConcurrency now actually drives LazyImageQueue.instance.maxConcurrent. Previously the config field was read but never applied to the singleton queue.
  • HyperAnimationController dispose hardening: Prevents controller double-dispose race conditions during fast widget rebuilds.

🔧 Improvements

  • Float carryover imagePixelOffset: FloatCarryover now carries an imagePixelOffset field computed from the originating section's layout. When a tall float image overhangs a virtualized section boundary, the offset records how many pixels were already painted — enabling future rendering of the remaining portion without repeating the top.
  • FloatCarryover value equality: Added proper operator==/hashCode so the _onFloatCarryover comparison correctly detects changes (including the new imagePixelOffset).
  • ComputedStyle.objectFit: New ObjectFit enum (cover, contain, fill, none, scaleDown) added to the computed style model and resolved from CSS declarations.

📱 Example App — Reader Demo Polish

  • Enhanced bookmarking with persistent state
  • Multi-theme reader (sepia, dark, custom)
  • Table of Contents side panel with smooth page transitions
  • Reading progress indicator

🛠️ Publish Tooling

  • publish.sh simplified: Instead of swapping entire pubspec files or patching path deps, now only removes the dependency_overrides: block before dart pub publish — the dependencies: section already has the correct version constraints (e.g. hyper_render_core: ^1.3.3). Restores original pubspec via git checkout -- after each package.
  • prepare_publish.sh updated: Same approach — strips dependency_overrides blocks instead of the old full-file swap with pubspec_publish_ready.yaml.

📝 Documentation

  • ROADMAP corrected: list-style-type and list-style-position were marked as incomplete ([ ]) despite being fully shipped in v1.3.1. Now correctly marked as [x]. object-fit moved from Backlog to Completed.
  • COMPARISON_MATRIX: Updated with latest feature parity data vs flutter_html, flutter_widget_from_html.
  • MIGRATION_GUIDE: Updated for 1.3.2 → 1.3.3 (new onMemoryPressure parameter, object-fit CSS support).
  • PERFORMANCE_TUNING: Added imageConcurrency tuning guidance.

📦 Packages Updated

Package Version
hyper_render 1.3.3
hyper_render_core 1.3.3
hyper_render_html 1.3.3
hyper_render_markdown 1.3.3
hyper_render_highlight 1.3.3
hyper_render_clipboard 1.3.3
hyper_render_devtools 1.3.3
hyper_render_math 1.3.3

🧪 Test Summary

  • 973 tests passing across all packages
  • New test files:
    • packages/hyper_render_core/test/object_fit_test.dart — object-fit CSS resolution
    • test/review_fixes_v1_3_3_test.dart — comprehensive v1.3.3 regression tests
    • test/v1_3_3_comprehensive_testing_test.dart — integration + edge case suite

70 files changed, 1853 insertions(+), 412 deletions(-)


Full changelog: https://github.com/brewkits/hyper_render/blob/main/CHANGELOG.md

HyperRender v1.3.2

04 Jun 16:54

Choose a tag to compare

[1.3.2] - 2026-05-18

Bug Fixes (Critical)

  • [DEADLOCK] LazyImageQueue no longer deadlocks on a synchronously-throwing loader — if a user-supplied HyperImageLoader threw before invoking its onLoad/onError callback, _active was never decremented; after maxConcurrent such throws the queue stopped processing every subsequent image until app restart. _startLoad now wraps the loader call in try/catch and routes any synchronous exception through the same idempotent error path used by the async callback.
  • [SECURITY] Sanitizer now validates ALL URL-bearing attributes — previously only href and src were checked, leaving poster, data, cite, background, longdesc, usemap, manifest, xlink:href, formaction, action, icon, and srcset as XSS bypass vectors (e.g. <video poster="javascript:...">). Added urlBearingAttributes constant and routes every match through isSafeUrl. srcset is split into candidates and each candidate's URL is validated independently.
  • [SECURITY] isTap no longer fires when the pointer never went down inside the widgethandleEvent previously treated downPosition == null as a valid tap, so a finger swiping into the widget from outside and lifting up would trigger onLinkTap on whatever fragment was under the lift point. Now requires BOTH a recorded down position AND a movement within tapSlop.
  • [BUG-1] Images no longer permanently disappear after a Low Memory WarningclearMemoryCaches() disposed the image cache but never re-triggered _loadImages(). Visible images were stuck in the empty-placeholder state until the user scrolled the section out of view and back to force a detach+attach cycle. The cache-clear path now re-enqueues image loads via LazyImageQueue so visible images reload through the normal priority pipeline.
  • [BUG-2] _hashSection now invalidates on attribute changes — the previous fingerprint only hashed text content + child count, so changing only <img src="a.jpg"><img src="b.jpg"> (or class/id/style) produced the same hash. _mergeSections would silently reuse the stale DocumentNode, freezing dynamic UI at the first rendered version. The new recursive hash walks the subtree and includes tagName, type, text, atomic src/alt, all attributes (keys sorted), and per-depth child counts.
  • [BUG-3] Eliminated 1-frame layout flash with dangling floats_onFloatCarryover previously deferred the cross-section update via addPostFrameCallback + setState, so section N+1 always laid out once with empty initialFloats before the corrected pass. Added onRenderBoxReady callback on HyperRenderWidget and VirtualizedChunk; _HyperViewerState keeps a Map<int, RenderHyperBox> registry and pushes new floats directly onto section N+1's RenderObject during section N's layout, so the pipeline owner picks up the change in the same frame.
  • [C-1] HyperSelectionOverlay now forwards config, pluginRegistry, enableComplexFilters — plugins, custom link schemes, keyframe animations and filter settings were silently ignored in sync+selectable and paged+selectable modes. All three params are now accepted by HyperSelectionOverlay and forwarded to the inner HyperRenderWidget.
  • [C-2] Fixed GPU memory leak in image cache_imageCache was missing an onEvict callback, so ui.Image GPU textures were never disposed when entries were evicted from the LRU. Added onEvict: (ci) => ci.image?.dispose() to free GPU memory promptly on eviction.
  • [C-3] Removed dead _parseIsolate / _parseReceivePort code — these fields were declared but never assigned, making _cancelParsing() a no-op. Cleaned up unused dart:isolate import and fields; _parseId counter remains the mechanism for discarding stale parse results.
  • [C-4] TextPainter global cache now respects HyperRenderConfig.textPainterCacheSize — was hardcoded to 500 regardless of config (default 5000). Added RenderHyperBox.setGlobalTextCacheSize() static method; HyperViewer calls it in initState and didUpdateWidget.

Bug Fixes (High)

  • [H-1] HyperRenderConfig.operator== and hashCode now include useMicrotaskParsing — changing only this field no longer fails to trigger a re-parse.
  • [H-2] ComputedStyle.copyWith() now copies _explicitlySet — previously the result had an empty explicit-set, causing inheritFrom() to overwrite all copyWith'd properties with parent styles, breaking the CSS cascade.
  • [H-3] _containsFloatChild detects float:left (no space) and Bootstrap/Tailwind class namesfloat:left, float-left, float-right, float-start, float-end, pull-left, pull-right are now detected, preventing incorrect section splits in virtualized mode.
  • [H-4] isSafeUrl() blocks file:, mhtml:, and about: schemes — these can access local filesystem, trigger MHTML exploits, or enable sandbox-escape via about:blank on Android/iOS.

Bug Fixes (Medium)

  • [M-1] _effectiveConfig is now cached — was allocating a new HyperRenderConfig on every build() call (every scroll frame). Cache is invalidated when renderConfig, allowedCustomSchemes, or document keyframes change.
  • [M-2] HyperViewer.fromNode now accepts pluginRegistry and onError — previously hardcoded to null, making plugins and error handling unavailable for pre-parsed AST consumers.
  • [M-3] _buildPagedContent no longer allocates a discarded HyperRenderWidget — restructured to if/else so only one widget is built per page in selectable mode.
  • [M-4] _TextPainterKey now includes wordSpacing — two fragments with identical text but different word-spacing no longer share the same TextPainter, preventing incorrect layout widths.

Performance (Low)

  • [L-1] LazyImageQueue._findQueued is now O(1) — added _urlToQueued secondary index; previously O(N) causing O(N²) batch behavior with many simultaneous image loads.
  • [L-2] _hasDetailFragments flag replaces O(N) scanperformLayout no longer scans all fragments to check for <details> elements; flag is set during tokenization.

Fixes (Low)

  • [L-3] _splitIntoSections no longer overwrites existing node parents — changed child.parent = current to if (child.parent == null) child.parent = current to avoid corrupting ancestor-chain traversal on reused section nodes.
  • [L-4] Removed dead _draggingHandle field from HyperSelectionOverlayState.

Correctness & Robustness

  • Hash collision resilience on Web_accumulateHashParts now also mixes in text.length for every TextNode, significantly reducing the chance that two long-but-distinct strings hash to the same slot on the JS target (where Object.hashAll has weaker dispersion than the Dart VM).
  • computeMinIntrinsicWidth handles icon fonts, emoji, and dingbats — the previous "longest-by-char-count word" heuristic miscalculated when a single PUA glyph (Material Icons, Font Awesome) or emoji renders far wider than a Latin letter. When the fragment contains any code point in U+E000–U+F8FF, U+2600–U+27BF, or U+1F000+, the entire fragment is measured instead of just the longest word.
  • RenderHyperBox.detach() now cancels shimmer state — a ListView item that detached mid-shimmer (scrolled out of cache) and later re-attached kept a stale _shimmerEpoch, producing a 1-frame phase jump on re-mount. The frame callback is now cancelled and _shimmerEpoch reset.

New

  • HyperRenderConfig.useRepaintBoundary (default true) — opt out of the outer-section RepaintBoundary wrapper. RenderHyperBox is already an internal repaint boundary, so this is mostly an escape hatch for very low-RAM Android devices (≤ 1.5 GB) rendering image-heavy long documents with a custom small virtualizationChunkSize, where many concurrent GPU layers could exhaust VRAM before the texture cache evicts.

Second-Pass Senior Review (2026-05-18 → 2026-05-19)

A second multi-disciplinary review (PM/BA/SA/principal mobile) surfaced a further batch of issues addressed in this same release. Highlights:

Security

  • UrlSafety consolidated in hyper_render_core/util/url_safety.dart — root HtmlSanitizer.isSafeUrl and the hyper_render_markdown sub-package's URL gate previously had independent copies that drifted: the sub-package missed file:/mhtml:/about:. Both now delegate to the shared helper; no future drift is possible.
  • HtmlAdapter defence-in-depth URL gate<img src> and <a href> are now routed through UrlSafety.isSafe even when the upstream HtmlSanitizer is bypassed (callers that invoke HtmlAdapter().parse() directly or render with sanitize: false). Blocked href collapses to #; blocked src collapses to ''.
  • hyper_render_clipboard filename hardening (path traversal)_getFilenameFromUrl already stripped path separators from URL-decoded filenames, but saveImageBytes(filename:) and shareImageBytes(filename:) concatenated caller-supplied strings raw. Every save/share path now runs through a single _sanitiseFilename helper.
  • Markdown inline HTML pre-sanitised — when HyperViewer.markdown(sanitize: true) (default) is used with enableInlineHtml: true (default), raw <script>/<style>/<iframe> blocks are now stripped via HtmlSanitizer before reaching the markdown parser, so they can no longer flash as visible text or become a self-rendering plugin's XSS surface.

Layout & Selection

  • Unbounded-width crash fixedRenderHyperBox.performLayout and _computeHeightForWidth clamp _maxWidth to a finite fallback when the constraint is double.infinity (Row without Expanded, horizontal SingleChildScrollView). Before this, _FlexFragment.layout propagated infinity into a BoxConstraints(minWidth: ∞) and tripped Flutter's minWidth < double.infinity assertion.
  • text-overflow: ellipsis no longer leaks hidden text via copyFragment.ellipsisVisibleLength track...
Read more

v1.3.1 — Decouple plugins, CSS list-style & background, selection performance

04 Jun 16:54

Choose a tag to compare

⚠️ Migration from 1.3.0

hyper_render_clipboard and hyper_render_math are now opt-in — no longer bundled in the root hyper_render package. Add them explicitly if needed:

dependencies:
  hyper_render: ^1.3.1
  hyper_render_clipboard: ^1.3.1   # image copy/save/share
  hyper_render_math: ^1.3.1        # LaTeX/MathML

If you don't use either feature, just bump the version — no other changes required.


What's New

New CSS Properties

  • list-style-type: disc, circle, square, decimal, decimal-leading-zero, lower-alpha, upper-alpha, lower-latin, upper-latin, lower-roman, upper-roman, none
  • list-style-position: inside / outside
  • list-style shorthand
  • background-repeat: repeat, repeat-x, repeat-y, no-repeat, space, round
  • background-position: keyword and percentage values

Performance

  • Selection drag: getSelectionRects() cached (1× per event, was 3×)
  • Auto-scroll speed now proportional to finger distance from edge
  • HyperTeardropHandlePainter deduplicated — exported from hyper_render_core

Bug Fixes

  • Edge-to-edge images: width: 100% now truly fills container (no internal margin)
  • Android build: no longer requires compileSdk = 35 workaround for default usage

Packages & Docs

  • All 7 sub-package CHANGELOGs updated with [1.3.1] entries
  • All READMEs: ecosystem cross-link table, corrected version references, fixed plugin API docs
  • Migration guide updated for 1.3.0 → 1.3.1

Full changelog: https://github.com/brewkits/hyper_render/blob/main/CHANGELOG.md

v1.3.0 — Math plugin, CSS cascade fix, customCss for Markdown/Delta

04 Jun 16:54

Choose a tag to compare

What's New in v1.3.0

This is a significant release that ships a new first-party math rendering package, fixes two CSS correctness bugs reported by users, completes the hyper_render_math package with full documentation and examples, and consolidates all improvements from the v1.2.x patch cycle.


🆕 New Package: hyper_render_math

A first-party plugin package for rendering LaTeX / MathML formulas inside HyperRender documents.

  • MathNodePlugin handles both <math> and <latex> tags via the HyperNodePlugin API
  • Renders using flutter_math_fork — KaTeX-quality output
  • Supports display mode (block, centered) and inline mode (flows inside text lines) via isInline
  • Formula source passed through src attribute or tag text content
  • Ships with LICENSE, CHANGELOG, example app, and pub.dev topics
final registry = HyperPluginRegistry()..register(const MathNodePlugin());

HyperViewer(
  html: r'<math src="x = \frac{-b \pm \sqrt{b^2-4ac}}{2a}" />',
  pluginRegistry: registry,
)

🐛 Bug Fixes

Issue #9 — CSS width/height overridden by HTML attributes (#9)

Root cause: render_hyper_box_layout.dart used node.intrinsicWidth ?? node.style.width, so HTML presentation attributes (width="345") always shadowed CSS rules (img { width: 600px }). This violated the CSS cascade spec where author stylesheets take priority over element attributes.

Fix: Swapped priority to node.style.width ?? node.intrinsicWidth at all 4 layout paths in render_hyper_box_layout.dart — normal layout, secondary layout pass, float layout (image loaded), and float layout (image loading placeholder).

<!-- Before: width was 345px regardless of CSS -->
<!-- After: CSS wins, width is 600px -->
<img src="photo.jpg" width="345" height="345">
<style>img { width: 600px; height: 300px; }</style>

Issue #8customCss ignored for Markdown and Delta content (#8)

Root cause: In hyper_viewer.dart, the cssToApply variable was only populated inside the if (contentType == html) block. For Markdown and Delta content types, customCss was silently discarded — the StyleResolver received an empty string.

Fix: Initialized cssToApply = widget.customCss ?? '' before the content-type check, so all three content types (html, markdown, delta) correctly receive and apply custom CSS rules.

// Now works for Markdown and Delta too
HyperViewer(
  content: markdownContent,
  contentType: HyperContentType.markdown,
  customCss: 'a { color: #0066cc; } h1 { font-size: 28px; }',
)

Additional fixes

  • <hr> returned wrong node type: html_adapter.dart now returns a BlockNode with a solid bottom border instead of LineBreakNode
  • AtomicNode.svg() wrong constructor params: Fixed named parameter mismatch in html_adapter.dart
  • Duplicate [1.3.0] CHANGELOG headers: Consolidated across all sub-packages

🏗️ Infrastructure & Quality

  • Tests relocated to sub-packages: html_adapter_test.dart, markdown_adapter_test.dart, code_highlighter_test.dart moved into their respective package test/ directories
  • hyper_render_highlight example: Corrected class reference to DefaultCodeHighlighter
  • publish.sh / prepare_publish.sh: Updated for v1.3.0 and now include hyper_render_math in all publish/analysis steps
  • All READMEs updated to reference ^1.3.0
  • 1,637 tests passing, 0 failures (+ 5 math package tests)
  • flutter analyze — 0 issues across all packages

📦 Packages in this release

Package Version
hyper_render 1.3.0
hyper_render_core 1.3.0
hyper_render_html 1.3.0
hyper_render_markdown 1.3.0
hyper_render_highlight 1.3.0
hyper_render_clipboard 1.3.0
hyper_render_devtools 1.3.0
hyper_render_math 1.3.0 (new)

Upgrading

dependencies:
  hyper_render: ^1.3.0
  # or individual packages:
  hyper_render_core: ^1.3.0
  hyper_render_math: ^1.3.0  # new

No breaking changes. Drop-in upgrade from v1.2.x.

v1.2.2 — 8 Bug Fixes + Android Build Fix

04 Jun 16:54

Choose a tag to compare

What's Changed

🐛 Bug Fixes

  • Android build failure (example/android/build.gradle.kts): irondash_engine_context 0.5.5 compiled against android-31 conflicts with androidx.fragment:1.7.1 (minCompileSdk=34). Added subprojects { compileSdk = 35 } override. Closes #5.
  • SVG invisible with sanitize: true (html_sanitizer.dart): Added atomic SVG sanitization path that preserves structure while stripping <script> and dangerous attributes.
  • HyperRenderConfig identity-compare (hyper_render_config.dart): Added operator== / hashCode — prevents unnecessary re-layouts on every frame when _effectiveConfig merges @keyframes into a new object.
  • selectable toggle ignored (hyper_viewer.dart): didUpdateWidget now creates/disposes VirtualizedSelectionController correctly when selectable changes.
  • Deep-link taps silently blocked (hyper_viewer.dart): _safeOnLinkTap now checks both allowedCustomSchemes AND renderConfig.extraLinkSchemes.
  • CSS change bypassed section cache (hyper_viewer.dart): _sectionHashes now reset in didUpdateWidget when customCss changes.
  • Markdown/Delta rendered as single section (hyper_viewer.dart): Added _splitIntoSections() — large Markdown/Delta docs now chunk correctly in virtualized/paged mode.
  • renderConfig change partially detected (hyper_viewer.dart): didUpdateWidget now uses full value equality instead of only comparing virtualizationChunkSize.
  • CSS float class names not detected (html_adapter.dart): _containsFloatChild now recognises Bootstrap/Tailwind patterns (float-left, pull-right, alignleft, etc.).

🔧 CI Fixes

  • benchmark.yml: Fixed JS SyntaxError from backtick-quoted fixture names inside template literal — use process.env instead of direct interpolation.
  • benchmark.yml + golden.yml: Added pull-requests: write permission for comment-posting steps.
  • test.yml: Guard sub-package test steps with [ -d test ] check — hyper_render_markdown, hyper_render_highlight, hyper_render_clipboard have no test directories.

Full Changelog: https://github.com/brewkits/hyper_render/blob/main/CHANGELOG.md

v1.2.0 — Plugin API, Paged Mode, Incremental Layout

04 Jun 16:54

Choose a tag to compare

What's new in v1.2.0

✨ New Features

Plugin API — First-class extensibility for custom HTML tags:

final registry = HyperPluginRegistry()
  ..register(MyChartPlugin())
  ..register(MyMapPlugin());

HyperViewer(html: content, pluginRegistry: registry)

Paged mode — Built-in e-book / reader UI:

final controller = HyperPageController();
HyperViewer(
  html: content,
  mode: HyperRenderMode.paged,
  pageController: controller,
)

Incremental layout — Dirty-flag fingerprinting means ~90% fewer rebuilds for live-updating feeds.

CSS @Keyframes — Full animation support with custom keyframe lookup from <style> tags.

♿ Accessibility (WCAG 2.1 AA)

  • <img alt="…"> → discrete SemanticsNode at image rect (WCAG 1.1.1)
  • aria-label on <a> elements honored (WCAG 4.1.2)

🏗️ Refactor

  • Removed 31 duplicate files from root lib/src/ — canonical source now in hyper_render_core
  • LazyImageQueue singleton consolidated (single shared instance)

🐛 Bug Fixes

  • XSS: javascript: URLs blocked in HTML, Markdown, and Delta adapters
  • display:none elements no longer produce layout fragments
  • Selection-vs-scroll conflict resolved
  • Context menu position clamped to visible bounds

📦 Infra

  • All packages: sdk >=3.5.0, csslib ^1.0.2, flutter_lints ^5.0.0
  • share_plus ^12.0.0, Android AGP 8.12.1, 16 KB page alignment
  • 36 new tests for v1.2.0 features

Migration

See MIGRATION_GUIDE for details. The public API is backwards-compatible — upgrading from v1.1.x requires no code changes.

Upgrade

dependencies:
  hyper_render: ^1.2.0

v1.1.2 — Binary Search Selection · DevTools v1.0.0 · 3-Pipeline CI

04 Jun 16:54

Choose a tag to compare

What's New

⚡ O(log N) Binary Search Text Selection

Hit-testing now uses _lineStartOffsets[] precomputed at layout time. Selection stays instant even on 1,000-line documents — no more O(N) linear scan per touch event.

🈶 Ruby Clipboard Format

Fully-selected ruby fragments are copied as base(ふりがな) (e.g. 東京(とうきょう)). Partial selections copy base text only, keeping character offsets consistent. 5 ruby selection pipeline bugs fixed that caused offset desynchronisation for all content after a ruby fragment.

🔭 hyper_render_devtools v1.0.0 — First Full Release

  • UDT Tree inspector — browse the full document model in Flutter DevTools
  • Computed Style panel — see inherited vs. declared values and specificity winners
  • Float region visualizer — highlight floated-block boundaries in layout
  • Demo mode — explore the inspector without a live app

🔁 3-Pipeline CI Architecture

Pipeline Trigger Purpose
Pre-flight (analyze.yml) Every PR/push dart format + flutter analyze --fatal-infos, skip docs-only
Core Validation (test.yml) PR: selective per-package on ubuntu-22.04 Fast gate < 5 min
Core Validation (test.yml) Push to main: 3-OS × 2-channel matrix Full platform coverage
Visual regression (golden.yml) Every PR/push Pixel-stable, pinned Noto fonts
Layout regression (benchmark.yml) Every PR/push Hard 16 ms / 60 FPS budget per fixture

🧪 Golden Tests — Float · RTL · CJK Coverage

9 new pixel-stable test cases:

  • Float layout: float_left, float_right, float_clear
  • RTL/BiDi: rtl_arabic, rtl_hebrew, rtl_mixed
  • CJK + Ruby: cjk_ruby, cjk_kinsoku, float_cjk

📊 Layout Regression Benchmark

6 HTML fixtures (simple paragraph → 100-paragraph article) with hard millisecond budgets run on every PR. Any fixture exceeding 16 ms (60 FPS) fails the build.


Upgrade

dependencies:
  hyper_render: ^1.1.2

No breaking changes. All previous HyperViewer APIs remain identical.


Full details in CHANGELOG.md.

v1.1.0

04 Jun 16:54

Choose a tag to compare

What's new

Rendering

  • Premium visual polish: precision borders, skeleton shimmer gradient, adaptive selection colors
  • Typography overhaul — font features (ligatures, proportional figures), consistent TextHeightBehavior, retina-ready FilterQuality.medium on all images
  • Anti-aliasing explicitly enabled on all paint operations

Bug fixes

  • Copy/paste across block elements (<li>, <h3>, <p>) now correctly inserts newlines
  • Fix _sameLinkContext() guard — incorrect link tap targets when fragments merge across <a> boundaries
  • Fix float crash on unconstrained parent width
  • Fix TapGestureRecognizer leak when document is replaced
  • Fix _fragmentChildMap O(N) scan → O(1) lookup in paint cycle
  • Fix _nodeRectCache O(N²) accessibility rect computation → O(N)
  • Fix display:none not respected in _tokenizeNode and _collectAtomicChildren

pub.dev

  • All sub-packages bumped to v1.1.0 (html, markdown, highlight, clipboard, devtools)
  • screenshots: field added with 6 demo GIFs visible in pub.dev gallery
  • vector_math updated to ^2.2.0
  • README rewritten with absolute image URLs (GIFs now render correctly on pub.dev)
  • Package description optimized for search discoverability

Tests

  • 678 tests passing, 0 failures
  • Added virtualized mode integration tests
  • Added real URL assertions to link tap tests
  • Updated golden images to match current rendering output

Demos

  • New CJK languages demo
  • Why HyperRender showcase with live 16-feature comparison matrix

Install

dependencies:
  hyper_render: ^1.1.0

Quick start

import 'package:hyper_render/hyper_render.dart';

HyperViewer(
  html: articleHtml,
  onLinkTap: (url) => launchUrl(Uri.parse(url)),
)

HyperRender v1.0.0

04 Jun 16:53

Choose a tag to compare

HyperRender v1.0.0 — Initial Public Release

A custom RenderObject-based engine that renders HTML, Markdown, and Quill Delta natively on Flutter Canvas — without WebViews, without widget trees.

What's included

  • CSS Float layout — text wrapping around floated images, architecturally impossible in widget-tree renderers
  • CJK typography — Ruby/Furigana with proper centering, Kinsoku line-breaking across the full line
  • Continuous text selection — select across headings, paragraphs, and table cells without widget-boundary breaks
  • CSS Variables + calc() — full custom property cascade
  • Flexbox and CSS Grid layout
  • Smart table layout — two-pass W3C column-width algorithm, colspan/rowspan, three overflow strategies
  • <details>/<summary> — collapsible sections, no JavaScript required
  • Multi-format input — HTML, Markdown, Quill Delta
  • Built-in XSS sanitizationjavascript:, vbscript:, SVG data URIs, expression() blocked by default
  • Virtualized renderingListView.builder mode for large documents
  • Screenshot exportGlobalKey.toPngBytes()
  • WebView fallback via HtmlHeuristics.isComplex()

Packages published

Package pub.dev
hyper_render https://pub.dev/packages/hyper_render
hyper_render_core https://pub.dev/packages/hyper_render_core
hyper_render_html https://pub.dev/packages/hyper_render_html
hyper_render_markdown https://pub.dev/packages/hyper_render_markdown
hyper_render_highlight https://pub.dev/packages/hyper_render_highlight
hyper_render_clipboard https://pub.dev/packages/hyper_render_clipboard

Benchmarks (macOS Desktop, Apple Silicon, Flutter release mode)

Document Parse time
1 KB 27 ms
10 KB 69 ms
50 KB 276 ms

Mobile performance has not been independently verified — run benchmarks on your own hardware.