diff --git a/.pubignore b/.pubignore
index ac9cb9c..fb5939c 100644
--- a/.pubignore
+++ b/.pubignore
@@ -71,3 +71,12 @@ coverage/
# macOS artifacts
**/.DS_Store
**/Pods/
+
+# Sub-packages are published on their own; the root package depends on
+# hyper_render_core through pub.dev, so shipping their sources only doubles
+# the archive size.
+packages/
+
+# Screen-recording entry point for the README GIFs (reads a local /tmp file);
+# nothing imports it.
+example/lib/demo_auto_player.dart
diff --git a/CHANGELOG.md b/CHANGELOG.md
index af61ff5..b29705e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 1.13.1
+
+Packaging and documentation only; no code changes in the library.
+
+- **Smaller download:** the archive no longer ships `packages/` (the sub-packages are published on their own and `hyper_render_core` is a normal pub.dev dependency) or the GIF-recording entry point `example/lib/demo_auto_player.dart`. About 32 MB → 18 MB.
+- **Docs corrected:** every `widgetBuilder: (context, node) { ... }` example (README, MIGRATION, security and performance guides) did not compile. The callback is `Widget? Function(UDTNode node)`. `node.isImage` does not exist; examples use `node.tagName == 'img'`.
+- **Docs no longer over-claim:** `box-sizing` and `position: relative` were listed as supported. They are parsed but not applied (`width` always sizes the content box; `top`/`left` do not move the element). The CSS matrix, `SUPPORTED_HTML.md` and `LIMITATIONS.md` now say so, and the docs-sync test guards both rows.
+
## 1.13.0
- **Character-range highlight boxes:** `RenderHyperBox.getBoxesForCharRange(charStart, charEnd)` returns the rects of a character range, for drawing your own highlights (search hits, annotations, read-aloud). Offsets are local to each `RenderHyperBox`, so in virtualized / `auto` mode (>10k chars) they restart at 0 per chunk. Also new: `RenderHyperBox.debugLineFragments()` for the laid-out fragment geometry. By [@DrkXo](https://github.com/DrkXo) ([#26](https://github.com/brewkits/hyper_render/pull/26)).
diff --git a/MIGRATION.md b/MIGRATION.md
index f20585b..8555385 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -114,7 +114,7 @@ HtmlWidget(
// HyperRender
HyperViewer(
html: html,
- widgetBuilder: (context, node) {
+ widgetBuilder: (node) {
if (node is AtomicNode && node.tagName == 'iframe') return MyWidget();
return null; // fall through to default rendering
},
diff --git a/README.md b/README.md
index b040a5f..322182b 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ Already using `flutter_html`? You don't need to rewrite your widget tree or lear
```dart
// 1. In your pubspec.yaml:
// dependencies:
-// hyper_render: ^1.13.0
+// hyper_render: ^1.13.1
// 2. In your Dart file — replace this single line:
// ❌ import 'package:flutter_html/flutter_html.dart';
@@ -68,7 +68,7 @@ Html(
```yaml
dependencies:
- hyper_render: ^1.13.0
+ hyper_render: ^1.13.1
```
```dart
@@ -432,7 +432,7 @@ ctrl.scrollToOffset(1200); // absolute pixel offset
```dart
HyperViewer(
html: html,
- widgetBuilder: (context, node) {
+ widgetBuilder: (node) {
if (node is AtomicNode && node.tagName == 'iframe') {
return YoutubePlayer(url: node.attributes['src'] ?? '');
}
diff --git a/doc/CSS_PROPERTIES_MATRIX.md b/doc/CSS_PROPERTIES_MATRIX.md
index 944f926..c751aa1 100644
--- a/doc/CSS_PROPERTIES_MATRIX.md
+++ b/doc/CSS_PROPERTIES_MATRIX.md
@@ -45,7 +45,7 @@ This document lists CSS property support in HyperRender.
| `border-style` | ✅ | solid, dashed, dotted, double, none | Custom styles beyond Flutter defaults |
| `border-color` | ✅ | All color formats | |
| `border-radius` | ✅ | px, % | All 4 corners + shorthand |
-| `box-sizing` | ✅ | border-box, content-box | Full support |
+| `box-sizing` | ❌ | — | Parsed, not applied: `width` always sizes the content box, so `border-box` is ignored (measured: `width:200px;padding:20px` wraps at 200px with or without it) |
| `aspect-ratio` | ✅ | W/H, bare number, auto | Applied to img/video sizing (v1.4.0) |
---
@@ -59,7 +59,7 @@ This document lists CSS property support in HyperRender.
| `opacity` | ✅ | 0-1 | |
| `overflow` | ⚠️ | hidden, visible | No scroll support |
| `position: static` | ✅ | static | Default |
-| `position: relative` | ✅ | relative | Supported |
+| `position: relative` | ❌ | — | Parsed, not applied: `top` / `left` offsets do not move the element |
| `position: absolute` | ❌ | — | Use `pluginRegistry` for overlay widgets |
| `position: fixed` | ❌ | — | Use `pluginRegistry` for overlay widgets |
| `top` / `right` / `bottom` / `left` | ❌ | — | Requires absolute/fixed; not supported |
diff --git a/doc/LIMITATIONS.md b/doc/LIMITATIONS.md
index a0e75cf..419b403 100644
--- a/doc/LIMITATIONS.md
+++ b/doc/LIMITATIONS.md
@@ -46,7 +46,7 @@ are silently ignored.
| Property | Limitation |
|----------|------------|
| `opacity` | Applied per-element; stacking context opacity not propagated |
-| `position: relative` | Supported but child `absolute` positioning is not |
+| `position: relative` / `box-sizing: border-box` | Parsed but not applied: no offset for `relative`, and `width` always sizes the content box |
| `calc()` | Arithmetic on px/em/rem only; `%` in calc not resolved |
| `text-align` | Center/right/justify on canvas paragraphs, LTR and box-level RTL (`textDirection: rtl`). An explicit `text-align` overrides RTL's default right edge; `justify` in RTL is not yet distributed (keeps the start edge) |
| `float` scope | A float reserves a box from the element's own `width`/`height`; it does **not** lay the floated element's content out inside that box. So float works for replaced elements (`
`) and for empty, explicitly sized boxes (`
`) — text wraps beside both. It does **not** work for an element sized by its own text: a floated `
` with content renders that content in normal flow while still reserving a phantom float box, which can make the block *taller* than not floating it; a floated inline `` (the drop-cap idiom) has no effect at all. Pinned by `test/flagship_execution_audit_test.dart` |
diff --git a/doc/PERFORMANCE_TUNING.md b/doc/PERFORMANCE_TUNING.md
index 4964bed..680f3c4 100644
--- a/doc/PERFORMANCE_TUNING.md
+++ b/doc/PERFORMANCE_TUNING.md
@@ -1,7 +1,7 @@
# Performance Tuning Guide
-Last Updated: June 2026
-Version: 1.4.0
+Last Updated: October 2026
+Version: 1.13.0
This guide helps you optimize HyperRender performance for different use cases.
@@ -40,7 +40,7 @@ DON'T:
HyperRender offers 3 render modes optimized for different scenarios:
-### 1. HyperRenderMode.sync (Default for small content)
+### 1. HyperRenderMode.sync (best for small content)
How it works:
- Parses HTML on main thread (synchronous)
@@ -170,7 +170,7 @@ void main() {
```dart
HyperViewer(
html: content,
- widgetBuilder: (context, node) {
+ widgetBuilder: (node) {
if (node is AtomicNode && node.tagName == 'img') {
final src = node.src;
if (src == null) return null;
@@ -421,8 +421,8 @@ HyperViewer(
)
// Lazy-load images
-widgetBuilder: (context, node) {
- if (node is AtomicNode && node.isImage) {
+widgetBuilder: (node) {
+ if (node is AtomicNode && node.tagName == 'img') {
return CachedNetworkImage(
imageUrl: node.src!,
placeholder: (context, url) => Shimmer(...),
@@ -513,8 +513,8 @@ HyperViewer(
```dart
HyperViewer(
html: content,
- widgetBuilder: (context, node) {
- if (node is AtomicNode && node.isImage) {
+ widgetBuilder: (node) {
+ if (node is AtomicNode && node.tagName == 'img') {
return CachedNetworkImage(
imageUrl: node.src!,
memCacheWidth: 800,
diff --git a/doc/README.md b/doc/README.md
index e49f842..a719a60 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -1,6 +1,6 @@
# HyperRender Documentation
-Welcome to the documentation for **HyperRender v1.13.0** — the high-performance HTML/Markdown renderer for Flutter.
+Welcome to the documentation for **HyperRender v1.13.1** — the high-performance HTML/Markdown renderer for Flutter.
## Documentation Index
diff --git a/doc/SECURITY_BEST_PRACTICES.md b/doc/SECURITY_BEST_PRACTICES.md
index 4f8817b..5811ae1 100644
--- a/doc/SECURITY_BEST_PRACTICES.md
+++ b/doc/SECURITY_BEST_PRACTICES.md
@@ -270,7 +270,7 @@ When using `widgetBuilder`, validate the input:
```dart
HyperViewer(
html: content,
- widgetBuilder: (context, node) {
+ widgetBuilder: (node) {
// Validate node properties
if (node.tagName == 'custom-widget') {
final dataUrl = node.attributes['data-url'];
@@ -295,7 +295,7 @@ HyperViewer(
```dart
HyperViewer(
html: content,
- widgetBuilder: (context, node) {
+ widgetBuilder: (node) {
if (node is AtomicNode && node.isVideo) {
final videoUrl = node.src;
@@ -412,7 +412,7 @@ Defense:
allowedTags: const {'p', 'strong', 'em'}, // No 'img'
// Option 2: Proxy images through your server
-widgetBuilder: (context, node) {
+widgetBuilder: (node) {
if (node is AtomicNode && node.tagName == 'img') {
final src = node.src;
if (src != null && src.startsWith('https://yourcdn.com/')) {
diff --git a/doc/SUPPORTED_HTML.md b/doc/SUPPORTED_HTML.md
index 4dd8d2a..2a30ea1 100644
--- a/doc/SUPPORTED_HTML.md
+++ b/doc/SUPPORTED_HTML.md
@@ -97,7 +97,7 @@ or the `fallbackBuilder` parameter to delegate to a WebView or other renderer.
| `border` | width, style, color |
| `border-style` | solid, dashed, dotted, double, none |
| `border-radius` | px, % |
-| `box-sizing` | border-box, content-box |
+| `box-sizing` | **Not applied** — `width` always sizes the content box |
| `overflow` | visible, hidden |
### Layout
@@ -107,7 +107,7 @@ or the `fallbackBuilder` parameter to delegate to a WebView or other renderer.
| `display` | block, inline, inline-block, none, flex, grid, table |
| `float` | left, right, none (Full wrapping support) |
| `clear` | left, right, both, none |
-| `position` | **relative only** |
+| `position` | **Not applied** — `static` flow only (no `relative` offsets, `absolute`, `fixed`) |
| Flexbox | Containers and items, incl. wrapping flex on a dedicated `RenderFlexWrap` (v1.9.0). Not applied: `align-content`; `flex-basis` drives wrapping containers only |
| CSS Grid | `grid-template-columns/rows` (px, fr, auto, repeat), `grid-column/row` span, gap (v1.2.0). Not applied: `grid-auto-flow`, `align-content` |
@@ -162,4 +162,4 @@ or the `fallbackBuilder` parameter to delegate to a WebView or other renderer.
---
-*Last updated: April 29, 2026 — HyperRender v1.3.0*
+*Last updated: October 10, 2026 — HyperRender v1.13.0*
diff --git a/example/lib/formula_demo.dart b/example/lib/formula_demo.dart
index a857cae..2e9f753 100644
--- a/example/lib/formula_demo.dart
+++ b/example/lib/formula_demo.dart
@@ -519,10 +519,9 @@ FormulaWidget(
color: DemoColors.warning),
_CodeSnippet(
code: r'''// Apply flutter_math_fork to ALL formulas in a document
-HyperViewer(
- html: myDeltaJson,
- format: HyperRenderFormat.delta,
- widgetBuilder: (context, node) {
+HyperViewer.delta(
+ delta: myDeltaJson,
+ widgetBuilder: (node) {
if (node is AtomicNode && node.tagName == 'formula') {
final formula = node.attributes['data'] ?? '';
return Math.tex(formula,
diff --git a/pubspec.yaml b/pubspec.yaml
index d5cbd56..e7b7e8a 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: hyper_render
description: "The only Flutter HTML/Markdown renderer with CSS float layout, crash-free text selection at any document size, CJK Ruby typography, and AI/LLM token streaming."
-version: 1.13.0
+version: 1.13.1
homepage: https://github.com/brewkits/hyper_render
repository: https://github.com/brewkits/hyper_render
issue_tracker: https://github.com/brewkits/hyper_render/issues
diff --git a/test/docs_matrix_sync_test.dart b/test/docs_matrix_sync_test.dart
index 0d2add9..324e2b8 100644
--- a/test/docs_matrix_sync_test.dart
+++ b/test/docs_matrix_sync_test.dart
@@ -48,6 +48,10 @@ const _executionVerified = [
const _mustNotBeFull = [
'position: absolute',
'position: fixed',
+ // Parsed into ComputedStyle and read by nothing in the render path; checked
+ // by laying out `width:200px;padding:20px` with and without border-box.
+ 'position: relative',
+ 'box-sizing',
'z-index',
// Parsed into ComputedStyle and read by nothing in the render path —
// measured, not assumed, by test/flagship_execution_audit_test.dart.