hyper_render 1.3.4
hyper_render: ^1.3.4 copied to clipboard
Render HTML/Markdown/Delta at 60 FPS. The only Flutter renderer with CSS float layout, crash-free text selection, and CJK Ruby typography. Drop-in flutter_html alternative.
Changelog #
1.3.4 - 2026-06-04 #
š§ Fixes & Optimizations #
- Static Analysis Compliance: Suppressed deprecated
SizeTransition.axisAlignmentlints with// ignore: deprecated_member_useto maintain backwards compatibility with older Flutter SDKs (>=3.10) while securing 160/160 points on pub.dev. - Dependency Widening: Widened
share_plusdependency constraint to^12.0.2 || ^13.0.0inhyper_render_clipboardand root packages to allow compatibility with latest stable release.
1.3.3 - 2026-06-04 #
š Production Readiness #
- Publishing Candidate: Comprehensive code cleanup, removal of unnecessary TODOs, and linting fixes.
- Test Coverage Verified: Passed 973 test cases covering unit, integration, system, performance, stress, and security testing.
- Dependency Resolution: Ensured all internal sub-packages (
hyper_render_math, etc.) are fully resolved and analyzed.
⨠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 thebackground-sizemapping;object-fitnow takes priority as the semantically correct property for replaced elements.
⨠New Features #
onMemoryPressurecallback:HyperViewernow exposes an optionalVoidCallback? onMemoryPressureparameter (available on all constructors: default,.delta,.markdown,.fromNode). Invoked after HyperRender clears its internal TextPainter, image, and painting caches in response todidHaveMemoryPressure. Enables host apps to release their own resources (video players, download queues, custom caches) in the same memory-pressure cycle.
š§ Improvements #
- Float carryover
imagePixelOffset:FloatCarryovernow carries animagePixelOffsetfield computed from the originating section's layout. When a tall float image overhangs a virtualized section boundary, the offset records how many pixels of the image were already painted ā enabling future visual rendering of the remaining portion in the next section without repeating the top. The_onFloatCarryovercomparison inHyperViewernow includesimagePixelOffsetto avoid missing updates.
š Documentation #
- ROADMAP corrected:
list-style-typeandlist-style-positionwere marked as incomplete ([ ]) despite being fully shipped in v1.3.1. Now correctly marked as[x].object-fitmoved from Backlog to Completed.
1.3.2 - 2026-05-18 #
Bug Fixes (Critical) #
- [DEADLOCK] LazyImageQueue no longer deadlocks on a synchronously-throwing loader ā if a user-supplied
HyperImageLoaderthrew before invoking its onLoad/onError callback,_activewas never decremented; aftermaxConcurrentsuch throws the queue stopped processing every subsequent image until app restart._startLoadnow 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
hrefandsrcwere checked, leavingposter,data,cite,background,longdesc,usemap,manifest,xlink:href,formaction,action,icon, andsrcsetas XSS bypass vectors (e.g.<video poster="javascript:...">). AddedurlBearingAttributesconstant and routes every match throughisSafeUrl.srcsetis split into candidates and each candidate's URL is validated independently. - [SECURITY]
isTapno longer fires when the pointer never went down inside the widget āhandleEventpreviously treateddownPosition == nullas a valid tap, so a finger swiping into the widget from outside and lifting up would triggeronLinkTapon whatever fragment was under the lift point. Now requires BOTH a recorded down position AND a movement withintapSlop. - [BUG-1] Images no longer permanently disappear after a Low Memory Warning ā
clearMemoryCaches()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 viaLazyImageQueueso visible images reload through the normal priority pipeline. - [BUG-2]
_hashSectionnow 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._mergeSectionswould silently reuse the staleDocumentNode, 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 ā
_onFloatCarryoverpreviously deferred the cross-section update viaaddPostFrameCallback + setState, so section N+1 always laid out once with empty initialFloats before the corrected pass. AddedonRenderBoxReadycallback onHyperRenderWidgetandVirtualizedChunk;_HyperViewerStatekeeps aMap<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 byHyperSelectionOverlayand forwarded to the innerHyperRenderWidget. - [C-2] Fixed GPU memory leak in image cache ā
_imageCachewas missing anonEvictcallback, soui.ImageGPU textures were never disposed when entries were evicted from the LRU. AddedonEvict: (ci) => ci.image?.dispose()to free GPU memory promptly on eviction. - [C-3] Removed dead
_parseIsolate/_parseReceivePortcode ā these fields were declared but never assigned, making_cancelParsing()a no-op. Cleaned up unuseddart:isolateimport and fields;_parseIdcounter 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). AddedRenderHyperBox.setGlobalTextCacheSize()static method;HyperViewercalls it ininitStateanddidUpdateWidget.
Bug Fixes (High) #
- [H-1]
HyperRenderConfig.operator==andhashCodenow includeuseMicrotaskParsingā 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, causinginheritFrom()to overwrite all copyWith'd properties with parent styles, breaking the CSS cascade. - [H-3]
_containsFloatChilddetectsfloat:left(no space) and Bootstrap/Tailwind class names āfloat:left,float-left,float-right,float-start,float-end,pull-left,pull-rightare now detected, preventing incorrect section splits in virtualized mode. - [H-4]
isSafeUrl()blocksfile:,mhtml:, andabout:schemes ā these can access local filesystem, trigger MHTML exploits, or enable sandbox-escape viaabout:blankon Android/iOS.
Bug Fixes (Medium) #
- [M-1]
_effectiveConfigis now cached ā was allocating a newHyperRenderConfigon everybuild()call (every scroll frame). Cache is invalidated whenrenderConfig,allowedCustomSchemes, or document keyframes change. - [M-2]
HyperViewer.fromNodenow acceptspluginRegistryandonErrorā previously hardcoded tonull, making plugins and error handling unavailable for pre-parsed AST consumers. - [M-3]
_buildPagedContentno longer allocates a discardedHyperRenderWidgetā restructured to if/else so only one widget is built per page in selectable mode. - [M-4]
_TextPainterKeynow includeswordSpacingā two fragments with identical text but differentword-spacingno longer share the sameTextPainter, preventing incorrect layout widths.
Performance (Low) #
- [L-1]
LazyImageQueue._findQueuedis now O(1) ā added_urlToQueuedsecondary index; previously O(N) causing O(N²) batch behavior with many simultaneous image loads. - [L-2]
_hasDetailFragmentsflag replaces O(N) scan āperformLayoutno longer scans all fragments to check for<details>elements; flag is set during tokenization.
Fixes (Low) #
- [L-3]
_splitIntoSectionsno longer overwrites existing node parents ā changedchild.parent = currenttoif (child.parent == null) child.parent = currentto avoid corrupting ancestor-chain traversal on reused section nodes. - [L-4] Removed dead
_draggingHandlefield fromHyperSelectionOverlayState.
Correctness & Robustness #
- Hash collision resilience on Web ā
_accumulateHashPartsnow also mixes intext.lengthfor everyTextNode, significantly reducing the chance that two long-but-distinct strings hash to the same slot on the JS target (whereObject.hashAllhas weaker dispersion than the Dart VM). computeMinIntrinsicWidthhandles 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 ā aListViewitem 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_shimmerEpochreset.
New #
HyperRenderConfig.useRepaintBoundary(defaulttrue) ā opt out of the outer-sectionRepaintBoundarywrapper.RenderHyperBoxis 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 smallvirtualizationChunkSize, 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
UrlSafetyconsolidated inhyper_render_core/util/url_safety.dartā rootHtmlSanitizer.isSafeUrland thehyper_render_markdownsub-package's URL gate previously had independent copies that drifted: the sub-package missedfile:/mhtml:/about:. Both now delegate to the shared helper; no future drift is possible.HtmlAdapterdefence-in-depth URL gate ā<img src>and<a href>are now routed throughUrlSafety.isSafeeven when the upstreamHtmlSanitizeris bypassed (callers that invokeHtmlAdapter().parse()directly or render withsanitize: false). Blockedhrefcollapses to#; blockedsrccollapses to''.hyper_render_clipboardfilename hardening (path traversal) ā_getFilenameFromUrlalready stripped path separators from URL-decoded filenames, butsaveImageBytes(filename:)andshareImageBytes(filename:)concatenated caller-supplied strings raw. Every save/share path now runs through a single_sanitiseFilenamehelper.- Markdown inline HTML pre-sanitised ā when
HyperViewer.markdown(sanitize: true)(default) is used withenableInlineHtml: true(default), raw<script>/<style>/<iframe>blocks are now stripped viaHtmlSanitizerbefore 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 fixed ā
RenderHyperBox.performLayoutand_computeHeightForWidthclamp_maxWidthto a finite fallback when the constraint isdouble.infinity(Row without Expanded, horizontalSingleChildScrollView). Before this,_FlexFragment.layoutpropagated infinity into aBoxConstraints(minWidth: ā)and tripped Flutter'sminWidth < double.infinityassertion. text-overflow: ellipsisno longer leaks hidden text via copy āFragment.ellipsisVisibleLengthtracks how many leading characters survive each truncation pass;getSelectedTextclamps the visible range against it and skips fully-suppressed fragments. State is reset at the top of every_performLineLayoutso a wider re-layout un-hides previously truncated text.- Selection-drag hit-test made lenient ā
_lineIndexAtaccepts aclampOutOfBoundsflag (truefor drag,falsefor tap). When a selection handle drags past the first/last line by a pixel, the index now snaps to the nearest line instead of returning-1and freezing. - Dead-code removal ā
_characterToFragment/_fragmentRangesfields inRenderHyperBoxwere populated each layout but never read; deleted along with theirclear()and populate loops. - Table cell block-content fallback ā when
cellContentBuilderisnulland a cell contains<div>/<p>children,_buildCellContentnow renders the inline run plus each block child via a defaultColumn/Textfallback instead of dropping the content. (Previously onlyHyperRenderWidgetcallers were safe.) - Table grid total-cell cap ā added
_kMaxTotalCells = 100 000. A pathological<table>whoserowCount Ć columnCountexceeds the cap now renders a visible "Table too large to render" placeholder instead of allocating an 8 MBnullgrid on the UI thread. HyperAnimatedWidgetcontroller lifecycle hardened ā switched fromSingleTickerProviderStateMixintoTickerProviderStateMixin(the previous mixin asserted on the secondcreateTicker()whendidUpdateWidgetrecreated the controller after a prop change). Start delay now uses a retainedTimerthat is cancelled ondidUpdateWidget/dispose, eliminating duplicateforward()calls in fast-rebuild scenarios (live editor typing).
Performance
HtmlAdapter.extractCssregex fast-path ā for inputs ā„ 32 KB or with no<styletag at all (the common Markdown/Delta case),extractCssnow skips the full html5lib parse on the UI thread and uses a focused regex. Saves 50ā300 ms on a 200 KB document on a mid-range Android.
Cross-package Polish
MarkdownContentParserrenamed toDefaultMarkdownParserā aligns withDefaultHtmlParser/DefaultCssParser. The old name remains as a@Deprecatedtypedef so existing callers compile; new code should use the new name.hyper_render_devtoolsnow has tests +dev_dependenciesblock āUdtSerializerround-trip + truncation cap +register()idempotency. Previously the package shipped zero tests.hyper_render_mathpubspec description normalised ā replaced the YAML folded-scalar (>) form with a plain string for consistency with the other six packages.pubspec_publish_ready.yamlandscripts/prepare_publish.shversion sync ā both now pin^1.3.2, eliminating the previous 1.3.1/1.3.2 mismatch that would have faileddart pub publish --dry-run.
Tests
71 new tests added across 11 files covering every fix above: URL safety scheme blocklist (core), HTML adapter URL gate, CSS parser edge cases, markdown GFM (tables/task-lists/autolinks/code-fence/heading), highlight edge cases (malformed source, 5 KB load, every theme), clipboard filename sanitisation, UDT serializer shape + truncation, animation controller race / dispose, table cell fallback + total-cell cap, extractCss perf, ellipsis copy + selection clamp regressions. Full suite: 1764 passing, 0 failing.
1.3.1 - 2026-05-14 #
ā ļø Migration from 1.3.0 #
hyper_render_clipboard and hyper_render_math are no longer transitive dependencies of hyper_render. If you use either, add them explicitly:
dependencies:
hyper_render: ^1.3.1
hyper_render_clipboard: ^1.3.1 # only if you use SuperClipboardHandler
hyper_render_math: ^1.3.1 # only if you use MathNodePlugin / LatexNodePlugin
⨠New CSS Properties #
list-style-type: All 11 marker types ādisc,circle,square,decimal,decimal-leading-zero,lower-alpha,upper-alpha,lower-latin,upper-latin,lower-roman,upper-roman,nonelist-style-position:inside/outsidelist-styleshorthand: parses type and position in any orderbackground-repeat:repeat,repeat-x,repeat-y,no-repeat,space,roundbackground-position: keyword (center,top left, etc.) and percentage values
š Performance #
- Selection rects cached:
getSelectionRects()now called once per drag event (was 3Ć) ā stored in_selectionRectsfield, eliminating redundant layout walks during selection drag - Auto-scroll proportional speed:
_autoScrollIfNearEdgescales 0ā20 px/frame based on finger proximity to edge (was fixed 15 px/frame) HyperTeardropHandlePainterdeduplicated: renamed, made public, and exported fromhyper_render_core; duplicate implementation in the virtualized overlay removed
š Bug Fixes #
- Edge-to-edge images:
width: 100%images now truly fill their container ā no internal margin offset
šļø Build Fixes #
- Decoupled native dependencies:
hyper_render_clipboardandhyper_render_mathremoved from roothyper_renderdefault dependencies ā eliminates thecompileSdk = 34Gradle requirement for basic usage - Removed outdated
compileSdkworkaround from example app's Android Gradle config
1.3.0 - 2026-05-03 #
⨠New Features #
- New Plugin Package:
hyper_render_math(packages/hyper_render_math): Added first-party support for mathematical formulas via LaTeX/MathML. It uses a customHyperNodePluginto render math content using theflutter_math_forkpackage. This milestone release consolidates all recent architectural improvements and bug fixes into a stable minor version.
š Performance & Stability #
- Test Coverage Optimization: Increased global test coverage to >85% with new comprehensive suites for parsers, adapters, and selection logic.
- Golden Test Alignment: Updated golden tests for consistent multi-platform rendering validation.
- Improved Widget Test Robustness: Updated
find.byType(HyperRenderWidget)assertions to handle multiple instances in the tree caused by virtualization and float nesting. Paint()memory optimization: Replaced inlinePaint()allocations in hot paint paths with reusable fields, reducing GC pressure during smooth scrolling.- Incremental layout hash collision fix: Improved the fingerprinting of document sections to prevent cache collisions on duplicate content.
š Bug Fixes #
- Markdown CRLF normalisation: Content is now normalised to LF before splitting, fixing stray carriage-return characters in code blocks on Windows.
- Virtualized Heading Protection: Added guards to prevent virtualized sections from orphaning headings (Heading Widow/Orphan protection).
- Config & Scheme Propagation: Fixed issues where
useMicrotaskParsingandallowedCustomSchemeswere dropped during CSS-driven config rebuilds. - Float Layout precision: Explicit CSS
widthandheightare now strictly respected for non-image float elements. - Selection logic refinement: Fixed edge cases for text selection across off-screen chunks in virtualized lists.
- Android & iOS Build compatibility: Modernized Gradle configuration and iOS project settings for better ARM64 simulator and modern SDK support.
- SVG Sanitization: Added an atomic SVG sanitization path to preserve structural elements while stripping dangerous attributes.
- Plugin Propagation: Ensured
pluginRegistryis correctly passed to nested renderers inside floated containers.
1.2.2 - 2026-04-02 #
š Bug Fixes #
- Android build failure with modern compileSdk (
example/android/build.gradle.kts):irondash_engine_context 0.5.5was compiled against android-31 but its transitiveandroidx.fragment:1.7.1dependency hasminCompileSdk=34, causing AGP 8'scheckAarMetadatato block the build. Added asubprojects { afterEvaluate { compileSdk = 35 } }override in the example's root Gradle file. README now documents the same one-line workaround for app-level projects. (#5) - SVG invisible with
sanitize: true(html_sanitizer.dart):<svg>was not indefaultAllowedTagsso the sanitizer unwrapped it, destroying the SVG structure. Added an atomic SVG sanitization path that strips<script>and dangerous attributes while preserving all structural SVG elements (path,circle,g,use, etc.). selectabletoggle ignored after build (hyper_viewer.dart): Togglingselectablefromfalseātruenever createdVirtualizedSelectionController, andtrueāfalsenever disposed it. Fixed indidUpdateWidget.- Deep-link tap silently blocked (
hyper_viewer.dart):_safeOnLinkTaponly checkedwidget.allowedCustomSchemesbut ignoredrenderConfig.extraLinkSchemes, causing deep-links registered viaHyperRenderConfigto be silently dropped. Both sources are now consulted. - CSS change didn't invalidate section cache (
hyper_viewer.dart):_hashSectionhashes only text content, so acustomCsschange that alters layout/appearance would incorrectly reuse cached sections._sectionHashesis now reset whenevercustomCsschanges indidUpdateWidget. - Markdown/Delta virtualized/paged mode rendered as single section (
hyper_viewer.dart): The sync fallback path wrapped the entire parsed document as one section, defeating virtualization. Added_splitIntoSections()to chunk Markdown/Delta documents at block boundaries, matching the HTML isolate path. renderConfigchange only partially detected (hyper_viewer.dart):didUpdateWidgetcompared onlyvirtualizationChunkSizeinstead of the fullHyperRenderConfig. Now uses full value equality (available since theoperator==fix) so any config change triggers a re-parse.- CSS float class names not detected (
html_adapter.dart):_containsFloatChildmissed Bootstrap/Tailwind float class names (float-left,pull-right,alignleft, etc.), causing premature section splits after float-containing blocks. Common class patterns are now detected heuristically.
1.2.1 - 2026-03-31 #
šļø Maintenance #
- Pub.dev compliance: Fixed internal dependency constraints to use version ranges instead of path dependencies in the published package.
- Virtualized screenshot description: Refined screenshot metadata in
pubspec.yamlfor better display on pub.dev. - Metadata cleanup: Removed stale comments and aligned topics for better discovery.
1.2.0 - 2026-03-30 #
⨠New Features #
-
Multi-tier Plugin API (
hyper_render_core): Third-party packages can now render arbitrary HTML tags as custom Flutter widgets viaHyperNodePlugin/HyperPluginRegistry.- Block tier (
isInline == false, default): widget takes full available width with CSS margins. - Inline tier (
isInline == true): widget flows inside text lines; intrinsic size measured inperformLayoutviagetMaxIntrinsicWidth / getMinIntrinsicHeight. - Register at startup:
HyperPluginRegistry()..register(MyPlugin())and pass toHyperViewer(pluginRegistry: ...).
- Block tier (
-
Dirty-flag incremental layout (
hyper_viewer.dart): Only re-layout sections whose content changed. EachDocumentNodechunk is fingerprinted withObject.hashAllover childtextContent; unchanged sections are reused on the next parse, andValueKey(hash)onRepaintBoundarylets Flutter skip re-layout and repaint entirely. ~90 0x0p+0yout rebuild reduction for live-updating feeds. -
Paged mode (
HyperRenderMode.paged):PageView.builder-based rendering, one document chunk per page. Suitable for e-book / epub / reader UIs.- Supply a
HyperPageControllerfor programmatic navigation (animateToPage,nextPage,previousPage,jumpToPage) andValueNotifier<int> currentPagefor reactive page indicators.
- Supply a
āæ Accessibility (WCAG 2.1 AA) #
- Image alt-text semantic nodes (
render_hyper_box_accessibility.dart):<img alt="ā¦">elements now produce a discreteSemanticsNodeat the image's layout rect. Screen-reader users can navigate to images element-by-element (WCAG 1.1.1 Non-text Content). Previously alt text only appeared in the flat document-level label. aria-labelon links honored (render_hyper_box_accessibility.dart): If an<a>element carries anaria-labelattribute, that value is used as the link's semantic label instead of its text content (WCAG 4.1.2 Name, Role, Value).
šļø Refactor ā Dead-code elimination #
- Removed 31 duplicate files from root
lib/src/that were identical or outdated copies of the canonical implementations inpackages/hyper_render_core. Rootlib/src/now contains only the 17 files that are genuinely unique to the root package (parsers, sanitizer,HyperViewer, virtualized selection,capture_extension). LazyImageQueuesingleton deduplication:lib/src/core/lazy_image_queue.dartwas a separate implementation that created a secondLazyImageQueue.instanceā meaningLazyImageQueue.instance.cancel()called from outsideHyperViewerhit a different singleton than the oneHyperViewerused internally. Root now re-exportsLazyImageQueuedirectly fromhyper_render_core(single shared instance).- Added missing v1.2.0 symbols to root re-export:
HyperRenderConfig,LazyImageQueue,HyperNodePlugin,HyperPluginRegistry,HyperPluginBuildContext,LoadingSkeleton,HyperErrorWidget,FloatCarryoverare now all accessible frompackage:hyper_render. - Consolidated double export: The redundant second
export 'package:hyper_render_core' show HyperRenderConfig'line was folded into the main re-export block.
š Bug Fixes #
-
Copy action produced empty clipboard (
virtualized_selection_overlay.dart,hyper_selection_overlay.dart): TheListener.onPointerDowncallback cleared the active selection before the Copy button'sonPressedcould fire, soClipboard.setDatareceived an empty string. Fixed by guardingclearSelection()behind a_showMenu/_showContextMenucheck (matching the pattern already used in the non-virtualized overlay). -
Context menu outside hit-testable bounds (
hyper_selection_overlay.dart,virtualized_selection_overlay.dart): When a selection was near the top of the widget the computedtopfor thePositionedmenu went negative.Stack(clipBehavior: Clip.none)allows visual overflow but Flutter hit-testing is still bounded by the parent ā the Copy button was unreachable. Fixed by clamping the top offset:.clamp(0.0, double.infinity). -
Scroll vs. text-selection conflict (
render_hyper_box.dart):handleEvent(PointerMoveEvent)bypassed the gesture arena and fired on every pointer move, creating accidental selections during scrolling. Removed raw-event selection tracking and moved selection initiation to aLongPressGestureRecognizerat the widget layer ā this correctly competes with the parent scroll view'sVerticalDragGestureRecognizer, so a quick swipe scrolls while a 500 ms hold begins a text selection (matching iOS/Android native behaviour). -
Virtualized copy menu never appeared (
virtualized_selection_overlay.dart): Per-chunkRenderHyperBox._selectionwas set by the old pointer-event tracking, butVirtualizedSelectionController(cross-chunk selection) was never populated, sohasSelectionremainedfalseand the menu was never shown. Fixed by routing the long-press start throughVirtualizedSelectionController.startSelection(). -
Selection Escape key fix (
hyper_selection_overlay.dart):Escapekey failed to clear selection because the internalFocusNodewasn't reliably focused after selection was established. Fixed by calling_focusNode.requestFocus()insidestartSelectionAt. -
constlint fix (hyper_render_widget.dart):HyperPluginBuildContextinstantiation changed toconstto silenceprefer_const_constructors.
1.1.4 - 2026-03-28 #
š Bug Fixes #
-
display:nonenot respected in renderer (render_hyper_box_layout.dart): Added early-return guard in_tokenizeNodeā elements withdisplay:noneno longer produce any layout fragments and are correctly hidden. Previously, elements styled withdisplay:none(e.g. Wikipedia[edit]section links) were still rendered. -
<hr>rendered as line break (html_adapter.dart):<hr>now correctly returns a styledBlockNodewith a top border (borderColor: #CCCCCC, borderWidth: 1px), matching browser behavior. Previously it was incorrectly treated identically to<br>. -
Whitespace-only space nodes dropped between inline elements (
html_adapter.dart): Text nodes consisting only of horizontal spaces (e.g." "between<b>text</b> <i>more</i>) were being silently dropped by.trim().isEmpty, causing missing word-separating spaces. Fixed to only drop nodes that contain newlines (structural indentation whitespace), not pure-space nodes. -
TextPaintercache hash collision (render_hyper_box.dart): The_LruCache<int, TextPainter>key was computed withObject.hash()which can collide for large documents with many distinct text styles, leading to wrong text metrics and subtle layout glitches. Replaced with a new_TextPainterKeyclass using full value equality over all 9 style fields.
1.0.0 - 2026-03-01 #
First stable release. Core features, plugin architecture, and cross-platform support are production-ready.
