Commit 085ebfd
feat: v1.1.2 — binary search selection, devtools v1.0.0, 3-pipeline CI
* fix(semantics): remove hand-rolled SemanticsNode creation that caused parentDataDirty assertion
assembleSemanticsNode() was calling _buildSemanticNodes() which created
brand-new SemanticsNode() objects on every call. Newly created nodes are
not attached to the SemanticsOwner, so node.updateWith() calls _adoptChild()
on them → parentDataDirty = true. PipelineOwner.flushSemantics() then walks
the full tree in debug mode and fires:
'!semantics.parentDataDirty': is not true
Fix: simplify assembleSemanticsNode() to only forward the children already
provided by Flutter's pipeline (child RenderObjects such as HyperDetailsWidget,
HyperTable, CodeBlockWidget). The full text label set in
describeSemanticsConfiguration() remains readable for TalkBack / VoiceOver.
Also removed the now-dead _buildSemanticNodes(), _buildNodeRectCache(),
_nodeRectCache, and related helpers to keep the codebase clean.
* perf/fix: three code-review improvements
- computeMinIntrinsicWidth: reuse two shared TextPainter instances (LTR
and RTL) across all fragments instead of allocating one per fragment,
then dispose both after the loop. Reduces native object churn on large
tables that call this method per cell.
- didHaveMemoryPressure: extend cleanup to also drain LazyImageQueue's
pending (not-yet-started) load queue and clear Flutter's own decoded-
image cache (PaintingBinding.imageCache). Previously only RenderHyperBox
caches were cleared, leaving pending network loads and GPU textures
uncollected on low-memory devices.
- LazyImageQueue.clearPending(): new public method that drains the pending
queue without touching in-flight loads. Called by didHaveMemoryPressure
and available to callers that need finer-grained memory control.
- _evaluateCalcExpr: emit a debugPrint (debug builds only, via assert) when
a calc() expression contains a % unit that cannot be resolved at parse
time, so developers can detect which expressions fall back silently.
* feat(devtools): complete hyper_render_devtools integration
Previously the devtools package existed but was entirely non-functional:
renderers were never registered (registry always empty), the UI showed
hardcoded placeholder data, and the devtools_extensions SDK was commented
out.
This commit wires all the pieces together:
HyperRenderDebugHooks (new, in hyper_render_core):
Static callback slots that RenderHyperBox calls at attach/detach/
performLayout. Avoids circular dependency — hyper_render_core never
imports hyper_render_devtools. Zero overhead in release builds (all
guards are behind kDebugMode / null checks).
RenderHyperBox auto-registration:
- Stable _debugId per instance (identityHashCode-based)
- attach() → HyperRenderDebugHooks.onRendererAttached
- detach() → HyperRenderDebugHooks.onRendererDetached (new override)
- performLayout end → HyperRenderDebugHooks.onLayoutComplete (lazy
getFragments/getLines getters — serialised only when DevTools reads)
service_extensions.dart:
- register() now injects all three hooks automatically — no per-widget
setup required from the user
- Added ext.hyperRender.getFragments — returns fragment list + line
list from the last layout pass
- Added ext.hyperRender.getPerformance — returns fragment/line counts
as baseline; pluggable via HyperRenderDebugHooks.getPerformanceData
for full PerformanceMonitor timing
- Switched from manual JSON string building to dart:convert jsonEncode
- Registry stores lastFragments/lastLines per renderer
devtools_ui/main.dart (full rewrite):
- Wraps app in DevToolsExtension widget (devtools_extensions SDK)
- All three tabs call real service extensions via
serviceManager.callServiceExtensionOnMainIsolate
- Renderer dropdown — select among multiple active renderers
- UDT Tree: clicking a node calls getNodeStyle and switches to Style tab
- Style: shows actual computed style from the running app
- Layout: shows real fragment list (capped at 200 rows) + line list +
performance summary
devtools_ui/pubspec.yaml:
- Added devtools_extensions: ^0.2.0
* docs/feat(heuristics): document form gap and add hasForms() check
HyperRender is read-only — it does not support <form>, <input>,
<select>, <textarea>, or submit buttons. This is a frequent BA gap
when requirements include "an article with an embedded survey at the
bottom."
Changes:
- Library-level doc block: explains the read-only constraint and
three recommended decision patterns:
A. WebView fallback for the whole screen
B. Native Flutter Form below HyperRender (preferred for new work)
C. Strip form tags via HtmlSanitizer when form is cosmetic only
- New HtmlHeuristics.hasForms(html): dedicated boolean check for
form/input/select/textarea/<button type="submit">. Lets BA-driven
routing logic express intent clearly rather than relying on the
broader isComplex() gate.
- hasUnsupportedElements() now delegates form detection to hasForms()
for a single source of truth.
* fix(P0): replace unbounded image cache with LRU eviction
RenderHyperBox._imageCache was a plain Map<String, CachedImage> with no
eviction policy. On a document with 100 high-res images, all decoded
ui.Image GPU textures were held for the entire lifetime of the widget,
causing unbounded GPU memory growth and OOM crashes on low-RAM Android
devices.
Fix: replace Map with the existing _LruCache<String, CachedImage> pattern,
mirroring how _textPainters already works.
- hyper_render_config.dart:
New imageCacheSize field (default 30, low-end 10, tablet 60+).
Added device-tier docs with recommended values.
- render_hyper_box.dart:
_imageCache is now _LruCache<String, CachedImage> with
onEvict: (ci) => ci.image?.dispose().
LRU evicts the oldest-untouched image and frees its GPU texture.
_disposeImages() simplified: _LruCache.clear() calls onEvict on
every entry — the manual dispose loop is no longer needed.
All _imageCache[src] = ... writes changed to _imageCache.put().
- render_hyper_box_paint.dart:
_paintImage now calls _imageCache.get(src) instead of _imageCache[src].
get() promotes the entry to most-recently-used, so images that are
actively being painted are never evicted mid-session.
On a null return (cache miss after eviction): show shimmer and
schedule _loadImage(src) via addPostFrameCallback so state is never
mutated inside paint(). The re-fetch deduplicates via LazyImageQueue.
* perf(P0): eliminate regex re-compilation and intrinsic-width O(N) jank
## Problem 1 — StyleResolver: 43 inline RegExp instantiations
Every call to _parseColor, _parseGradient, _calculateSpecificity,
_matchesSelector, _extractPseudoClasses, etc. created a new RegExp
object. Dart compiles regex to a DFA on first instantiation; re-creating
the same pattern in a hot path allocates a new object and re-compiles
every call. With 5000+ styled nodes this produced tens of thousands of
short-lived RegExp objects and measurable GC pressure.
Fix: new _Re abstract final class holding 33 static final compiled
patterns grouped by purpose (selectors, specificity, combinators,
pseudo-classes, value functions, colors, layout, filters). All 43
inline RegExp(...) calls replaced with _Re.xxx references. Zero
functional change — patterns are identical.
## Problem 2 — computeMinIntrinsicWidth: O(totalWords) → O(fragments)
The previous implementation split every fragment's text on whitespace
and called TextPainter.layout() for each individual word. For a 3000-
word article this means ~3000 synchronous layout calls on the main
thread, causing 200–400 ms jank when the widget is wrapped in
IntrinsicWidth or DataTable.
Fix: for each text fragment, find the single longest word by character
count (an O(W) scan with no layout) and measure only that one word.
The longest measured word is almost always the longest-character word;
the only edge case is short wide-glyph strings vs long narrow-glyph
strings, which is negligible for real prose. TextPainter calls drop
from O(totalWords) to O(fragmentCount) — one call per fragment.
Added _kWhitespaceSplitter library-level final to avoid re-compiling
the \s+ regex on every computeMinIntrinsicWidth call.
* fix(core): P1 edge-case hardening — tap slop, deep-link schemes, calc depth
- Replace hardcoded tapThreshold=8.0 with computeHitSlop(event.kind,
GestureBinding.instance.gestureSettings) so tap detection matches
platform gesture physics (mouse: 1 px, touch: ~18 px).
- Add HyperRenderConfig.extraLinkSchemes (Set<String>) so apps can
permit their own deep-link schemes (e.g. 'myapp', 'shopee') without
bypassing the built-in safe set (http/https/mailto/tel).
- Cap _evaluateCalcInValue loop with _kMaxCalcDepth=8 to prevent an
adversarially crafted calc(calc(calc(...))) with 1000 nesting levels
from looping indefinitely; add a no-progress early-exit guard.
* fix(a11y,perf): P2 — viewport semantics for headings/links + skip measureFragments on details toggle
Accessibility (WCAG 2.1 AA):
- assembleSemanticsNode now builds individual SemanticsNodes for h1–h6
heading blocks (isHeader: true) and <a href> links (isLink: true +
onTap). TalkBack/VoiceOver users can now navigate headings by swipe
and activate links by double-tap.
- Regular paragraph text continues to be announced via the flat `label`
on the container node — no change in linear reading behaviour.
- Semantic nodes are pooled in _cachedSemanticAnchorNodes (same pattern
as Flutter's RenderParagraph) to avoid recreating SemanticsNode objects
on every assembleSemanticsNode call, which would trigger the
parentDataDirty assertion in flushSemantics() in debug mode.
- Caps at _kMaxSemanticAnchors = 200 to guard against adversarially
large documents filling the accessibility tree.
Performance (details relayout):
- Split the single needsLineLayout condition into two branches:
fragmentsOrWidthChanged (full rebuild including _measureFragments) and
hasDetailsFragments-only (skip _measureFragments, re-run line layout).
- Each frame of a <details> expand/collapse animation previously called
_measureFragments() — the most expensive step (TextPainter layout).
Text content and constraint width are unchanged during animation, so
TextPainter output is identical frame-to-frame and measurement can be
safely skipped.
* fix: Fix some bugs
* fix: token-based image cancellation, table OOM cap, surrogate-safe text splitting
- lazy_image_queue: replace URL-based cancelAll with per-subscriber int tokens;
add _inFlight set to deduplicate concurrent loads; distribute ui.Image.clone()
per subscriber and dispose original to prevent GPU leak
- render_hyper_box: track tokens in _imageTokens set; cancel all on dispose
- render_table: clamp colspan/rowspan to _kMaxSpan=1000 to prevent OOM;
remove double LayoutBuilder wrapping so nested tables answer intrinsic height queries
- render_hyper_box_layout: snap breakIndex off UTF-16 low surrogates (0xDC00–0xDFFF)
in _splitTextFragment and _forceSplitTextFragment to avoid invalid lone-surrogate
strings crashing TextPainter; fix characterOffset double-counting trimmedLeading
* fix(selection): prevent StackOverflow when releasing scroll hold after handle drag
HoldScrollActivity.cancel() triggers goBallistic → beginActivity, which
disposes the old HoldScrollActivity synchronously. dispose() fires
onHoldCanceled = _releaseScrollHold while _scrollHold is still non-null
(the null assignment hadn't run yet), causing infinite mutual recursion.
Fix: capture the hold reference and null the field first, then cancel.
* fix(selection): replace GestureDetector with Listener to unblock Copy button
GestureDetector.onTapDown entered the gesture arena and fired _handleTap
unconditionally on every pointer-down, clearing the selection and hiding
the context menu before TextButton.onPressed could fire copySelection().
Replace with a Listener (no arena participation) that guards on
_showContextMenu: when the menu is visible the pointer-down is ignored,
letting the Copy/Share buttons receive the tap and copy the text.
* test(p2): add 42-test suite covering P2 fixes; update accessibility demo with WCAG 2.1 AA features
- Add packages/hyper_render_core/test/p2_fixes_test.dart with 42 tests
covering: CSS inline styles, flex layout, grid layout, heading
anchors, HyperDetailsWidget expand/collapse, and link tap + scheme
whitelisting. All 680 tests pass.
- Update example/lib/accessibility_demo.dart to demo the new P2
capabilities: heading navigation semantics (isHeader), link activation
semantics (isLink + onTap), and extraLinkSchemes toggle that lets
users whitelist the myapp:// deep-link scheme at runtime.
* feat(selection): cross-chunk Selection Orchestrator for virtualized mode
Adds VirtualizedSelectionController — a ChangeNotifier that owns a
global (chunkIndex, localOffset) selection spanning multiple independent
RenderHyperBox instances inside the virtualized ListView.
Key changes:
- RenderHyperBox: expose totalCharacterCount getter and public
getCharacterPositionAtOffset() wrapper (previously private)
- VirtualizedSelectionController: manages CrossChunkSelection state;
translates it into per-chunk HyperTextSelection; handles cross-chunk
handle dragging with closest-chunk fallback; getSelectedText() falls
back to DocumentNode.textContent for off-screen chunks
- VirtualizedChunk: thin StatefulWidget that registers/unregisters with
the controller after first layout and wires onSelectionChanged
- VirtualizedSelectionOverlay: Stack overlay with teardrop handles and
Copy/Select-All popup menu in the ListView coordinate space; freezes
ancestor scroll during handle drag (same as single-chunk overlay)
- HyperViewer: instantiates the controller in initState (selectable only);
replaces bare HyperRenderWidget in itemBuilder with VirtualizedChunk;
wraps the virtualized path with VirtualizedSelectionOverlay when
showSelectionMenu is true
Sync mode is completely unchanged — it still uses HyperSelectionOverlay.
* feat(svg): add native SVG rendering via flutter_svg
- Add flutter_svg ^2.0.0 dependency
- Implement buildSvgWidget() interceptor for inline <svg>, <img src="*.svg">, and data:image/svg+xml URIs
- Wire SVG builder into HyperViewer._effectiveWidgetBuilder (chains before user's widgetBuilder)
- Export buildSvgWidget from hyper_render.dart for composability
- Update Sprint3Demo SVG tab: remove "add flutter_svg manually" note — it's now built-in
- Add 10 unit tests covering all SVG rendering paths
* fix(demo): use value: instead of initialValue: in DropdownButtonFormField
* fix(float): add parse-time guard to avoid wasted space at chunk boundaries
Issue 1 — Infinite loop risk: already fixed (lines 1512-1513 / 1589-1591
in render_hyper_box_layout.dart clamp oversized floats to container width
before the search loop, ensuring O(1) termination).
Issue 2 — Float wasted space in virtualized mode:
- Add HtmlAdapter._containsFloatChild() — detects float:left/right img nodes
- parseToSections() now skips the section split immediately after a block
that contains a CSS-floated element, keeping the float and its successor
in the same RenderHyperBox so text wraps around the float correctly
- Add clarifying comment in performLayout() explaining the wasted-space
trade-off and the float.rect.bottom height extension (lines 963-967)
- Document the full FloatCarryover future work in doc/ROADMAP.md
- Add 3 unit tests covering float-guard and normal-split behaviour
* perf(kinsoku): replace O(N) string scan with O(1) Set<int> lookup tables
Replace kinsokuStart.contains(text[i]) (O(N) linear scan + heap String
allocation per character) with Set<int> lookup tables built once at
class-load time from the canonical kinsoku strings.
Key changes:
- _startCodes / _endCodes: static final Set<int> built via _buildCodeSet()
- cannotStartLine / cannotEndLine: use codeUnitAt(0) + Set.contains (O(1))
- canBreakBetween: Set + codeUnitAt — zero String allocation
- _canBreakAt (hot-path): Set.contains(codeUnitAt) — O(1), zero alloc
- findBreakPoint: delegates to _canBreakAt, eliminating O(N²) substring
allocations from the previous canBreakBetween(substring, substring) loop
All kinsoku characters are in the BMP (U+0000–U+FFFF), so codeUnitAt()
returns the exact Unicode code point with no surrogate handling needed.
* perf(kinsoku): upgrade Set<int> to Uint8List bitmask for O(1) direct-index lookup
Replace the Set<int> hash tables with a single 64 KB Uint8List(0x10000)
bitmask, encoding kinsoku-start (bit 0) and kinsoku-end (bit 1) categories
for every BMP code unit.
Why faster than Set<int>:
- Set.contains() computes a hash + may traverse a collision chain
- _table[codeUnit] is a single memory dereference — no hash, no branch
- 64 KB fits entirely in L2 cache; repeated layout-scan accesses are
served from cache, not RAM
- _canBreakAt() reads the table once per boundary position instead of
calling two separate Set.contains() — one load covers both categories
The _buildTable() builder iterates kinsokuStart/kinsokuEnd once at
class-load time; all public API signatures are unchanged.
* fix: table ANR guard + cross-chunk float continuity
## Table nesting depth guard (ANR prevention)
Add _TableNestingDepth InheritedWidget that propagates nesting depth
through the widget tree. HyperTable.build() checks _TableNestingDepth.of()
and returns _TableDepthExceededPlaceholder ('[table]') at depth ≥ 6.
The 2-pass layout algorithm (_RenderHyperTable.performLayout) has O(2^D)
complexity for nested tables: measuring column widths triggers each cell's
layout, doubling per level. The depth-6 cap matches browser behaviour and
prevents ANR on adversarial or poorly-authored HTML. No constructor
signatures changed — InheritedWidget propagates depth automatically.
## Cross-chunk float continuity
Implement float-state transfer between virtualised sections so text in
Chunk N+1 wraps around a float that began in Chunk N.
New types:
- FloatCarryover (in render_hyper_box_types.dart): direction, width,
overhangHeight — describes a float whose rect.bottom exceeds the
natural text height of its section.
New RenderHyperBox API:
- initialFloats: List<FloatCarryover> — seeds _leftFloats/_rightFloats
at the start of _performLineLayout for the inherited floats.
- danglingFloats: getter — computes FloatCarryover from floats that
extend past the last-line bottom after layout.
- onFloatCarryover callback — fired after each performLayout with the
current danglingFloats list.
Threading:
- HyperRenderWidget: initialFloats + onFloatCarryover parameters
- VirtualizedChunk: initialFloats + onFloatCarryover pass-through
- HyperViewer: _floatCarryovers state list + _onFloatCarryover handler
that triggers a minimal setState when carryover changes, causing only
the affected next-section item to rebuild.
* feat(table): vertical-align top/middle/bottom for table cells
Parse and apply CSS vertical-align / HTML valign attribute on table cells.
## Parsing
- resolver.dart: add 'vertical-align' case to _applyProperty() — maps
top/middle/bottom/baseline/text-top/text-bottom to HyperVerticalAlign
- resolver.dart: add _parseVerticalAlign() helper (mirrors _parseTextAlign)
- resolver.dart: read HTML 'valign' attribute before CSS rules (lower
priority than inline styles), so valign="middle" on <tr>/<td> works
Precedence: cell inline style > CSS class on cell > valign on cell >
CSS class on row > valign on row > default (top)
## Layout
- _TableCellParentData: add verticalAlign field (default: top)
- _TableCellSlot: carry verticalAlign through applyParentData
- HyperTable.build(): resolve effective vertical-align (cell > row > top);
uses HyperVerticalAlign.baseline as the "not explicitly set" sentinel
since ComputedStyle.verticalAlign defaults to baseline
- _positionCells: compute slack = rowSlotHeight − cellHeight, then:
top / baseline / text-top → dy = 0 (existing behaviour)
middle → dy = slack/2
bottom / text-bottom → dy = slack
Works correctly for rowspan > 1 cells: slot height is the sum of all
spanned rows plus inner borders, matching _computeRowHeights Pass 3.
* feat(table): add screen-reader semantics for tables and header cells
- Wrap each HyperTable in Semantics(container:true, label:'Table, N rows, M columns')
so TalkBack/VoiceOver announces the table structure on focus.
- Wrap <th> cells in Semantics(header:true) so screen readers distinguish
column/row headers from data cells.
- Uses only stable Flutter Semantics API available since SDK 3.10,
keeping compatibility with the declared pubspec lower-bound.
* fix: temp for CSS Keyframes/Animations
* feat: Update info, code, documents for release 1.1.2
* feat: v1.1.2 — binary search selection, devtools v1.0.0, 3-pipeline CI, golden tests
Core engine:
- O(log N) binary search hit-testing for text selection (_lineStartOffsets[])
- Ruby clipboard format: base(ふりがな) for full-fragment selection
- Ruby selection pipeline: 5 bug fixes (offset sync, paint, clipboard, rects)
DevTools (hyper_render_devtools v1.0.0):
- First full release: UDT Tree inspector, Computed Style panel, Float region visualizer
- Demo mode: explore inspector without a live app
- README, CHANGELOG, LICENSE, example added
CI/CD — 3-pipeline architecture:
- Pipeline 1 (analyze.yml): Pre-flight — dart format + flutter analyze --fatal-infos,
dorny/paths-filter for 8 path categories, skips on docs-only changes (< 2 min target)
- Pipeline 2 (test.yml): Core Validation — PR uses single ubuntu-22.04 runner with
per-package selective testing; push to main runs full 3-OS × 2-channel matrix
- Pipeline 2 (coverage.yml): push-to-main only, pinned Flutter 3.29.2 + ubuntu-22.04
- Visual regression (golden.yml): pinned ubuntu-22.04 + Noto fonts, update-goldens job
- Performance regression (benchmark.yml): layout regression guard — 6 fixtures with
hard 16 ms (60 FPS) budget, fails PR on any regression
Golden tests:
- 9 new test cases: Float layout (left/right/clear), RTL/BiDi (Arabic/Hebrew/mixed),
CJK + Ruby (kinsoku, float+CJK) — all pinned pixel-stable
Documentation:
- README: CI badges, devtools in extension packages table, O(log N) + layout CI guard
in architecture section, roadmap updated (devtools + @Keyframes shipped)
- doc/ROADMAP.md: completed items updated through v1.1.2
- .gitignore: add .metadata, .flutter-plugins, benchmark/results/, analyze_report.txt,
pubspec_publish_ready.yaml; replace overbroad *.txt
Packages: all sub-packages bumped to 1.1.2 with issue_tracker field
---------
Co-authored-by: Nguyễn Tuấn Việt <viet.nguyen@sungrove.co.jp>1 parent 896d060 commit 085ebfd
File tree
114 files changed
+9231
-1797
lines changed- .github/workflows
- benchmark
- benchmark_runner
- linux
- flutter
- runner
- windows
- flutter
- runner
- doc
- example
- ios
- lib
- macos
- Flutter
- lib
- src
- core
- model
- parser
- html
- plugins
- style
- utils
- widgets
- packages
- hyper_render_clipboard
- hyper_render_core
- lib
- src
- core
- interfaces
- model
- style
- widgets
- test
- hyper_render_devtools
- devtools_ui
- lib
- example
- lib/src
- hyper_render_highlight
- hyper_render_html
- lib/src
- hyper_render_markdown
- scripts
- test
- golden
- goldens
- integration
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
114 files changed
+9231
-1797
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
2 | 22 | | |
3 | 23 | | |
4 | 24 | | |
5 | | - | |
| 25 | + | |
6 | 26 | | |
7 | | - | |
| 27 | + | |
8 | 28 | | |
9 | 29 | | |
10 | | - | |
11 | | - | |
12 | | - | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
13 | 44 | | |
14 | 45 | | |
15 | | - | |
| 46 | + | |
16 | 47 | | |
17 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
18 | 86 | | |
| 87 | + | |
19 | 88 | | |
20 | 89 | | |
| 90 | + | |
21 | 91 | | |
22 | 92 | | |
23 | 93 | | |
24 | | - | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
25 | 106 | | |
26 | 107 | | |
27 | | - | |
28 | | - | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
29 | 112 | | |
30 | | - | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
31 | 116 | | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
| 117 | + | |
| 118 | + | |
40 | 119 | | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
47 | 123 | | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
52 | 131 | | |
53 | | - | |
| 132 | + | |
54 | 133 | | |
55 | 134 | | |
56 | | - | |
| 135 | + | |
57 | 136 | | |
58 | 137 | | |
59 | | - | |
| 138 | + | |
60 | 139 | | |
| 140 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | | - | |
4 | | - | |
5 | | - | |
6 | | - | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
7 | 15 | | |
8 | | - | |
9 | | - | |
| 16 | + | |
| 17 | + | |
10 | 18 | | |
11 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
12 | 22 | | |
13 | | - | |
14 | | - | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
15 | 27 | | |
16 | 28 | | |
17 | | - | |
18 | | - | |
19 | | - | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
20 | 35 | | |
21 | 36 | | |
22 | | - | |
| 37 | + | |
23 | 38 | | |
24 | 39 | | |
25 | | - | |
| 40 | + | |
26 | 41 | | |
27 | 42 | | |
| 43 | + | |
28 | 44 | | |
29 | 45 | | |
30 | 46 | | |
31 | 47 | | |
32 | 48 | | |
33 | 49 | | |
34 | | - | |
| 50 | + | |
| 51 | + | |
35 | 52 | | |
36 | | - | |
37 | | - | |
| 53 | + | |
38 | 54 | | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
46 | 59 | | |
47 | | - | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
48 | 103 | | |
49 | 104 | | |
50 | | - | |
51 | | - | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
52 | 108 | | |
53 | | - | |
54 | | - | |
| 109 | + | |
| 110 | + | |
55 | 111 | | |
56 | 112 | | |
57 | 113 | | |
58 | | - | |
59 | | - | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
60 | 143 | | |
61 | 144 | | |
62 | | - | |
63 | | - | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
0 commit comments