feat: configurable injection of js and css - #174
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a cross-platform API for registering additional JavaScript and CSS assets to be injected into EPUB HTML resources, extending the existing built-in flutterReadiumTools.js / flutterReadiumTools.css helper injection. It introduces a new shared InjectionAsset model and wires the method-channel plumbing, with Android implementing the injection into HTML resources and iOS adding storage + method handlers.
Changes:
- Added
InjectionAsset(JSON-serializable) and new platform-interface methods:setCssInjections/setJavaScriptInjections. - Android: implemented configurable injection by composing built-in helpers with extra injection assets and making injection idempotent via marker replacement.
- iOS: added method-channel handlers and state for injection lists; updated docs/changelog and extended tooling scripts.
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| flutter_readium/test/flutter_readium_test.dart | Updates mock platform to satisfy new platform interface methods. |
| flutter_readium/lib/flutter_readium.dart | Exposes new public API methods on FlutterReadium. |
| flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift | Adds iOS-side injection asset parsing and method-channel handlers; stores injection lists. |
| flutter_readium/CHANGELOG.md | Documents the new injection feature and platform support. |
| flutter_readium/android/src/main/kotlin/dk/nota/flutterreadium/ReadiumReader.kt | Passes configured injections into the HTML injection pipeline. |
| flutter_readium/android/src/main/kotlin/dk/nota/flutterreadium/ReadiumExtensions.kt | Implements injection asset URL building and marker-based replace/insert logic. |
| flutter_readium/android/src/main/kotlin/dk/nota/flutterreadium/PublicationChannel.kt | Adds method-channel handlers to set injections from Dart. |
| flutter_readium_platform_interface/lib/src/shared/injection_asset.dart | Introduces the shared Dart InjectionAsset model and JSON serialization. |
| flutter_readium_platform_interface/lib/src/shared/index.dart | Exports InjectionAsset from the shared model index. |
| flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart | Implements method-channel calls for the new injection setters. |
| flutter_readium_platform_interface/lib/flutter_readium_platform_interface.dart | Adds the new injection methods to the platform interface contract. |
| CLAUDE.md | Updates documented repo toolchain facts and Flutter version update guidance. |
| bin/format | Extends formatting script behavior (formatting scope + added analysis calls). |
| .vscode/settings.json | Adjusts tool auto-approve configuration entries. |
Suppressed comments (2)
flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift:233
- The arguments coming from Dart are bridged as
[[String: Any]](withNSNullfor nulls). Casting to[[String: Any?]]will often fail and silently fall back to[], making injection registration a no-op on iOS. Also, mapping via the force-casting initializer can crash; use acompactMapwith the failable initializer.
case "setCssInjections":
let items = call.arguments as? [[String: Any?]] ?? []
self.cssInjections = items.map { InjectionAsset(from: $0) }
result(nil)
case "setJavaScriptInjections":
flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift:43
- These new injection lists are stored and can be set over the method channel, but they are never read when building the WKWebView user scripts. As a result, the iOS implementation appears to accept injections without actually injecting them into EPUB resources.
/// Extra CSS assets injected alongside the built-in helpers.
var cssInjections: [InjectionAsset] = []
/// Extra JavaScript assets injected alongside the built-in helpers.
var javaScriptInjections: [InjectionAsset] = []
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| /// Registers extra CSS assets to inject into every EPUB HTML resource, | ||
| /// in addition to the built-in `flutterReadiumTools.css`. | ||
| /// Call before opening a publication so the injections are active when the reader view is created. | ||
| Future<void> setCssInjections(List<InjectionAsset> injections) => _platform.setCssInjections(injections); | ||
|
|
||
| /// Registers extra JavaScript assets to inject into every EPUB HTML resource, | ||
| /// in addition to the built-in `flutterReadiumTools.js`. | ||
| /// Call before opening a publication so the injections are active when the reader view is created. | ||
| Future<void> setJavaScriptInjections(List<InjectionAsset> injections) => | ||
| _platform.setJavaScriptInjections(injections); |
| } | ||
| } | ||
|
|
||
| PluginLog.d(TAG, "Injecting files into: $filename") |
| init(from map: [String: Any?]) { | ||
| assetPath = map["assetPath"] as! String | ||
| packageName = map["package"] as? String | ||
| } |
| /// Identifies a Flutter asset (JS or CSS file) to inject into EPUB HTML resources. | ||
| /// | ||
| /// [assetPath] is the asset path as declared in `pubspec.yaml`, e.g. `assets/custom.js`. | ||
| /// [package] is the pub package that owns the asset, or `null` for app-level assets. | ||
| /// The file type is inferred from the path extension (`.js` or `.css`). | ||
| @immutable | ||
| class InjectionAsset implements JSONable { | ||
| const InjectionAsset({required this.assetPath, this.package}); | ||
|
|
||
| factory InjectionAsset.fromJson(Map<String, dynamic> json) => InjectionAsset( | ||
| assetPath: json['assetPath'] as String, | ||
| package: json['package'] as String?, | ||
| ); |
Restored dev branch from before rewritting history