react-native-parity-blur 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/LICENSE +20 -0
  2. package/ParityBlur.podspec +20 -0
  3. package/README.md +198 -0
  4. package/android/build.gradle +57 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/com/parityblur/AndroidBlurCalibration.kt +82 -0
  7. package/android/src/main/java/com/parityblur/BlurEngine.kt +132 -0
  8. package/android/src/main/java/com/parityblur/ParityBlurDebug.kt +24 -0
  9. package/android/src/main/java/com/parityblur/ParityBlurPackage.kt +17 -0
  10. package/android/src/main/java/com/parityblur/ParityBlurView.kt +542 -0
  11. package/android/src/main/java/com/parityblur/ParityBlurViewManager.kt +161 -0
  12. package/android/src/main/java/com/parityblur/PipelineMath.kt +202 -0
  13. package/android/src/main/java/com/parityblur/PixelCopySnapshotProvider.kt +108 -0
  14. package/android/src/main/java/com/parityblur/SoftwareSnapshotProvider.kt +173 -0
  15. package/android/src/main/java/com/parityblur/WindowBlurContext.kt +116 -0
  16. package/android/src/test/java/com/parityblur/MiniJson.kt +179 -0
  17. package/android/src/test/java/com/parityblur/PipelineFixturesTest.kt +222 -0
  18. package/ios/BlurEngine.swift +106 -0
  19. package/ios/ColorPipeline.swift +72 -0
  20. package/ios/MetalPresentationSurface.swift +51 -0
  21. package/ios/ParityBlurCoreView.swift +382 -0
  22. package/ios/ParityBlurDebug.swift +18 -0
  23. package/ios/ParityBlurView.h +14 -0
  24. package/ios/ParityBlurView.mm +135 -0
  25. package/ios/PipelineMath.swift +177 -0
  26. package/ios/SnapshotProvider.swift +128 -0
  27. package/ios/WindowBlurContext.swift +111 -0
  28. package/lib/module/BlurView.js +124 -0
  29. package/lib/module/BlurView.js.map +1 -0
  30. package/lib/module/ParityBlurViewNativeComponent.ts +82 -0
  31. package/lib/module/defaults.js +13 -0
  32. package/lib/module/defaults.js.map +1 -0
  33. package/lib/module/index.js +5 -0
  34. package/lib/module/index.js.map +1 -0
  35. package/lib/module/package.json +1 -0
  36. package/lib/module/pipeline/androidCalibration.js +55 -0
  37. package/lib/module/pipeline/androidCalibration.js.map +1 -0
  38. package/lib/module/pipeline/captureRect.js +151 -0
  39. package/lib/module/pipeline/captureRect.js.map +1 -0
  40. package/lib/module/pipeline/color.js +82 -0
  41. package/lib/module/pipeline/color.js.map +1 -0
  42. package/lib/module/pipeline/constants.js +65 -0
  43. package/lib/module/pipeline/constants.js.map +1 -0
  44. package/lib/module/pipeline/downsample.js +61 -0
  45. package/lib/module/pipeline/downsample.js.map +1 -0
  46. package/lib/module/pipeline/index.js +23 -0
  47. package/lib/module/pipeline/index.js.map +1 -0
  48. package/lib/module/pipeline/overlay.js +56 -0
  49. package/lib/module/pipeline/overlay.js.map +1 -0
  50. package/lib/module/pipeline/saturation.js +52 -0
  51. package/lib/module/pipeline/saturation.js.map +1 -0
  52. package/lib/module/pipeline/types.js +23 -0
  53. package/lib/module/pipeline/types.js.map +1 -0
  54. package/lib/module/pipeline/units.js +33 -0
  55. package/lib/module/pipeline/units.js.map +1 -0
  56. package/lib/module/presets.js +96 -0
  57. package/lib/module/presets.js.map +1 -0
  58. package/lib/module/types.js +4 -0
  59. package/lib/module/types.js.map +1 -0
  60. package/lib/typescript/package.json +1 -0
  61. package/lib/typescript/src/BlurView.d.ts +16 -0
  62. package/lib/typescript/src/BlurView.d.ts.map +1 -0
  63. package/lib/typescript/src/ParityBlurViewNativeComponent.d.ts +63 -0
  64. package/lib/typescript/src/ParityBlurViewNativeComponent.d.ts.map +1 -0
  65. package/lib/typescript/src/defaults.d.ts +12 -0
  66. package/lib/typescript/src/defaults.d.ts.map +1 -0
  67. package/lib/typescript/src/index.d.ts +5 -0
  68. package/lib/typescript/src/index.d.ts.map +1 -0
  69. package/lib/typescript/src/pipeline/androidCalibration.d.ts +42 -0
  70. package/lib/typescript/src/pipeline/androidCalibration.d.ts.map +1 -0
  71. package/lib/typescript/src/pipeline/captureRect.d.ts +84 -0
  72. package/lib/typescript/src/pipeline/captureRect.d.ts.map +1 -0
  73. package/lib/typescript/src/pipeline/color.d.ts +19 -0
  74. package/lib/typescript/src/pipeline/color.d.ts.map +1 -0
  75. package/lib/typescript/src/pipeline/constants.d.ts +52 -0
  76. package/lib/typescript/src/pipeline/constants.d.ts.map +1 -0
  77. package/lib/typescript/src/pipeline/downsample.d.ts +31 -0
  78. package/lib/typescript/src/pipeline/downsample.d.ts.map +1 -0
  79. package/lib/typescript/src/pipeline/index.d.ts +20 -0
  80. package/lib/typescript/src/pipeline/index.d.ts.map +1 -0
  81. package/lib/typescript/src/pipeline/overlay.d.ts +31 -0
  82. package/lib/typescript/src/pipeline/overlay.d.ts.map +1 -0
  83. package/lib/typescript/src/pipeline/saturation.d.ts +38 -0
  84. package/lib/typescript/src/pipeline/saturation.d.ts.map +1 -0
  85. package/lib/typescript/src/pipeline/types.d.ts +32 -0
  86. package/lib/typescript/src/pipeline/types.d.ts.map +1 -0
  87. package/lib/typescript/src/pipeline/units.d.ts +23 -0
  88. package/lib/typescript/src/pipeline/units.d.ts.map +1 -0
  89. package/lib/typescript/src/presets.d.ts +57 -0
  90. package/lib/typescript/src/presets.d.ts.map +1 -0
  91. package/lib/typescript/src/types.d.ts +116 -0
  92. package/lib/typescript/src/types.d.ts.map +1 -0
  93. package/package.json +167 -0
  94. package/src/BlurView.tsx +195 -0
  95. package/src/ParityBlurViewNativeComponent.ts +82 -0
  96. package/src/defaults.ts +20 -0
  97. package/src/index.ts +10 -0
  98. package/src/pipeline/androidCalibration.ts +64 -0
  99. package/src/pipeline/captureRect.ts +172 -0
  100. package/src/pipeline/color.ts +66 -0
  101. package/src/pipeline/constants.ts +65 -0
  102. package/src/pipeline/downsample.ts +74 -0
  103. package/src/pipeline/index.ts +20 -0
  104. package/src/pipeline/overlay.ts +46 -0
  105. package/src/pipeline/saturation.ts +65 -0
  106. package/src/pipeline/types.ts +37 -0
  107. package/src/pipeline/units.ts +32 -0
  108. package/src/presets.ts +103 -0
  109. package/src/types.ts +132 -0
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Canonical unit conversions (plan §5.1).
3
+ *
4
+ * INTERNAL MODULE. Pure functions, no side effects, no react-native imports.
5
+ *
6
+ * blurRadius = Gaussian sigma in dp (public unit)
7
+ * sigmaPx = blurRadius * displayScale (device pixels)
8
+ * sigmaSnapshot= sigmaPx / D (downsampled snapshot px)
9
+ *
10
+ * The blur backend always receives a parameter derived from sigmaSnapshot.
11
+ */
12
+ import type { Downsample } from './types.js';
13
+ /**
14
+ * Convert the public blurRadius (sigma in dp) to sigma in device pixels.
15
+ * Non-finite or negative inputs clamp to 0 (plan §28 safe clamping).
16
+ */
17
+ export declare function sigmaPxFromDp(blurRadiusDp: number, displayScale: number): number;
18
+ /**
19
+ * Convert device-pixel sigma to snapshot-domain sigma given a downsample
20
+ * factor D. D is a positive integer in {1,2,4,8}.
21
+ */
22
+ export declare function sigmaSnapshotFromPx(sigmaPx: number, downsample: Downsample): number;
23
+ //# sourceMappingURL=units.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"units.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/units.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAS,CAAC;AAE1C;;;GAGG;AACH,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAIhF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAGnF"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Material-like presets (plan §11, plan §41 / Milestone 8).
3
+ *
4
+ * A preset is nothing more than a fixed, explicit
5
+ * `{ blurRadius, saturation, overlayColor }` prop bundle. There is no
6
+ * hidden or platform-specific behavior: applying `BlurPresets.dark` is
7
+ * exactly equivalent to spreading that plain object onto `<BlurView>`
8
+ * yourself, and the resulting three numbers/strings go through the same
9
+ * calibrated pipeline as any other explicit props (docs/PIPELINE_SPEC.md).
10
+ * Because blurRadius/saturation/overlayColor are calibrated to match
11
+ * cross-platform (docs/CALIBRATION_REPORT.md — worst measured ΔE76 0.90,
12
+ * SSIM ≥ 0.983), a given preset renders the same on iOS and Android.
13
+ *
14
+ * These are *approximations* of the visual weight of iOS system materials
15
+ * (UIBlurEffect styles), chosen by eye against reasonable blurRadius /
16
+ * saturation / overlayColor values — they are not derived from, measured
17
+ * against, or guaranteed to match `UIVisualEffectView` output, and Android
18
+ * has no equivalent native material to match at all. Treat the names as a
19
+ * starting point, not a parity claim (plan §43 — nested/material stacking
20
+ * is deterministic, not an Apple material emulation).
21
+ *
22
+ * Usage:
23
+ * ```tsx
24
+ * <BlurView {...BlurPresets.dark} style={styles.sheet}>
25
+ * {children}
26
+ * </BlurView>
27
+ * ```
28
+ *
29
+ * Every field is a plain public prop, so you can still override any one of
30
+ * them by spreading the preset first and your own prop second:
31
+ * ```tsx
32
+ * <BlurView {...BlurPresets.dark} blurRadius={30} />
33
+ * ```
34
+ *
35
+ * Or, for the same effect without spreading, pass the `preset` prop
36
+ * directly to `<BlurView>` (`src/BlurView.tsx`) — it resolves to this same
37
+ * object client-side before any explicit blurRadius/saturation/overlayColor
38
+ * prop is applied on top of it. Both forms are pure JS convenience; neither
39
+ * adds a native prop.
40
+ */
41
+ /** One named preset: an explicit bundle of public BlurView props. */
42
+ export interface BlurPreset {
43
+ /** Gaussian sigma in dp — see `BlurViewProps.blurRadius`. */
44
+ blurRadius: number;
45
+ /** See `BlurViewProps.saturation`. */
46
+ saturation: number;
47
+ /** See `BlurViewProps.overlayColor`. */
48
+ overlayColor: string;
49
+ }
50
+ /** Keys of {@link BlurPresets}. */
51
+ export type BlurPresetName = 'ultraThin' | 'thin' | 'regular' | 'thick' | 'chrome' | 'light' | 'dark';
52
+ /**
53
+ * Named material-like preset table. See the module doc comment above for
54
+ * semantics and the "not a parity claim" caveat.
55
+ */
56
+ export declare const BlurPresets: Readonly<Record<BlurPresetName, BlurPreset>>;
57
+ //# sourceMappingURL=presets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../../src/presets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,qEAAqE;AACrE,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,mCAAmC;AACnC,MAAM,MAAM,cAAc,GACxB,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3E;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,UAAU,CAAC,CA2CpE,CAAC"}
@@ -0,0 +1,116 @@
1
+ import type * as React from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+ import type { BlurPresetName } from './presets.js';
4
+ /** Public blur mode (plan §20/§21). default: 'static'. */
5
+ export type BlurMode = 'static' | 'live';
6
+ /** Public quality tier (plan §13). default: 'balanced'. */
7
+ export type BlurQuality = 'high' | 'balanced' | 'performance';
8
+ /** Public downsample selector (plan §12). default: 'auto'. */
9
+ export type BlurDownsample = 'auto' | 1 | 2 | 4 | 8;
10
+ export interface BlurViewProps {
11
+ /**
12
+ * Gaussian sigma in dp (plan §5.1) — the same canonical unit on both
13
+ * platforms (iOS pt ≡ Android dp semantically). Internally converted to
14
+ * each native backend's own blur parameter (docs/PIPELINE_SPEC.md §2), so
15
+ * the same number produces calibrated matching visual weight on iOS and
16
+ * Android (worst measured ΔE76 0.90, SSIM ≥ 0.983 across the parity
17
+ * suite — docs/CALIBRATION_REPORT.md). `0` (or omitted) renders no blur.
18
+ * Non-finite or negative values clamp to 0 with a DEV warning.
19
+ * default: 0.
20
+ */
21
+ blurRadius?: number;
22
+ /**
23
+ * `'static'` captures the backdrop once (on mount, layout change, or a
24
+ * prop change that affects the capture) and presents a still blurred
25
+ * snapshot; call `refresh()` to recapture after the backdrop changes
26
+ * underneath it. `'live'` recaptures continuously (throttled by
27
+ * `maxFps`) while the view is on screen. default: 'static'.
28
+ */
29
+ mode?: BlurMode;
30
+ /**
31
+ * Named material-like preset (plan §11) resolved client-side to a base
32
+ * `{ blurRadius, saturation, overlayColor }` bundle before this
33
+ * component's own explicit props are applied on top — see
34
+ * `BlurPresets`/`BlurPresetName` in `presets.ts` for the exact values.
35
+ * Pure JS convenience: it does not add a native prop, and any of those
36
+ * three props passed explicitly alongside `preset` overrides the
37
+ * preset's value for that prop only. default: none.
38
+ */
39
+ preset?: BlurPresetName;
40
+ /**
41
+ * Straight-alpha color composited source-over the blurred + saturated
42
+ * result (docs/PIPELINE_SPEC.md §8) — the only tint anywhere in the
43
+ * pipeline; there is no hidden per-platform tint. An unparseable color
44
+ * resolves to "no overlay" with a DEV warning. default: 'transparent'
45
+ * (no overlay).
46
+ */
47
+ overlayColor?: string;
48
+ /**
49
+ * Post-blur saturation multiplier (docs/PIPELINE_SPEC.md §7), applied
50
+ * after blur and before the overlay: `1` = unchanged, `0` = Rec.709
51
+ * grayscale, values `> 1` boost saturation. Negative or non-finite values
52
+ * clamp to the default with a DEV warning. default: 1.
53
+ */
54
+ saturation?: number;
55
+ /**
56
+ * Consumer-friendly quality tier that biases automatic downsample
57
+ * selection (plan §13): `'high'` prefers 1x/2x snapshots, `'balanced'`
58
+ * prefers 2x/4x, `'performance'` prefers 4x/8x. Has no effect when
59
+ * `downsample` is an explicit numeric override. default: 'balanced'.
60
+ */
61
+ quality?: BlurQuality;
62
+ /**
63
+ * Snapshot downsample factor (plan §12). `'auto'` derives a factor from
64
+ * requested sigma, capture area, and `quality` (docs/PIPELINE_SPEC.md
65
+ * §4); `1 | 2 | 4 | 8` forces that exact factor for advanced tuning.
66
+ * default: 'auto'.
67
+ */
68
+ downsample?: BlurDownsample;
69
+ /**
70
+ * Upper bound on live-mode recapture rate, in frames per second. Ignored
71
+ * in `'static'` mode. Clamped to `[1, 120]` with a DEV warning outside
72
+ * that range. default: 30.
73
+ */
74
+ maxFps?: number;
75
+ /**
76
+ * Color rendered in place of a real blur wherever real blur is
77
+ * unavailable: Android below API 31 (no `RenderEffect`) or, on either
78
+ * platform, when the OS-level "Reduce Transparency" accessibility
79
+ * setting is on. Has no effect on a device/OS combination where real
80
+ * blur runs. default: none (renders fully transparent when the fallback
81
+ * path is active and no `fallbackColor` is set).
82
+ */
83
+ fallbackColor?: string;
84
+ /**
85
+ * Target view whose subtree should be captured as the blur backdrop,
86
+ * instead of the default (the content behind this `BlurView` within its
87
+ * own window) (plan §18). **Not implemented in v1** — accepted only to
88
+ * keep the public API shape stable for a later release; passing it
89
+ * currently has no native-side effect. Deferred to v1.1 — see
90
+ * docs/LIMITATIONS.md.
91
+ */
92
+ blurTarget?: React.RefObject<any>;
93
+ /** Content rendered on top of the blur; unaffected by it. */
94
+ children?: React.ReactNode;
95
+ /**
96
+ * Standard React Native view style. `borderRadius` (and other clipping
97
+ * styles) clip the blur output itself, not just the children (plan §31).
98
+ */
99
+ style?: StyleProp<ViewStyle>;
100
+ }
101
+ /**
102
+ * Imperative handle exposed via ref (plan §29).
103
+ */
104
+ export interface BlurViewRef {
105
+ /**
106
+ * Schedules a coalesced recapture of the backdrop on the next valid
107
+ * frame — fully implemented end-to-end on both native backends (not a
108
+ * no-op). Repeated calls before the pending capture runs coalesce into a
109
+ * single recapture. Primarily useful in `mode="static"` after the
110
+ * backdrop has changed; in `mode="live"` the view is already recapturing
111
+ * continuously, so `refresh()` has no additional effect beyond nudging
112
+ * the next scheduled frame.
113
+ */
114
+ refresh(): void;
115
+ }
116
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAW,CAAC;AAEhD,0DAA0D;AAC1D,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEzC,2DAA2D;AAC3D,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,aAAa,CAAC;AAE9D,8DAA8D;AAC9D,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEpD,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,QAAQ,CAAC;IAEhB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IAExB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IAEtB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAE5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAElC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;;;OAQG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB"}
package/package.json ADDED
@@ -0,0 +1,167 @@
1
+ {
2
+ "name": "react-native-parity-blur",
3
+ "version": "0.1.0",
4
+ "description": "Cross-platform native backdrop blur for React Native (Fabric)",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "react-native-parity-blur-source": "./src/index.ts",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "*.podspec",
22
+ "react-native.config.js",
23
+ "!ios/build",
24
+ "!android/build",
25
+ "!android/gradle",
26
+ "!android/gradlew",
27
+ "!android/gradlew.bat",
28
+ "!android/local.properties",
29
+ "!**/__tests__",
30
+ "!**/__fixtures__",
31
+ "!**/__mocks__",
32
+ "!**/.*"
33
+ ],
34
+ "scripts": {
35
+ "example": "yarn workspace react-native-parity-blur-example",
36
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
37
+ "prepare": "bob build",
38
+ "typecheck": "tsc",
39
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
40
+ "test": "jest"
41
+ },
42
+ "keywords": [
43
+ "react-native",
44
+ "ios",
45
+ "android",
46
+ "blur",
47
+ "backdrop-blur",
48
+ "glassmorphism",
49
+ "fabric",
50
+ "new-architecture"
51
+ ],
52
+ "repository": {
53
+ "type": "git",
54
+ "url": "git+https://github.com/lakhwindersingh/react-native-parity-blur.git"
55
+ },
56
+ "author": "Lakhwinder Singh <luckysingh781@gmail.com> (https://github.com/lakhwindersingh)",
57
+ "license": "MIT",
58
+ "bugs": {
59
+ "url": "https://github.com/lakhwindersingh/react-native-parity-blur/issues"
60
+ },
61
+ "homepage": "https://github.com/lakhwindersingh/react-native-parity-blur#readme",
62
+ "publishConfig": {
63
+ "registry": "https://registry.npmjs.org/"
64
+ },
65
+ "devDependencies": {
66
+ "@commitlint/config-conventional": "^21.0.2",
67
+ "@eslint/compat": "^2.1.0",
68
+ "@eslint/eslintrc": "^3.3.5",
69
+ "@eslint/js": "^10.0.1",
70
+ "@jest/globals": "^29.7.0",
71
+ "@react-native/babel-preset": "0.85.0",
72
+ "@react-native/eslint-config": "0.85.0",
73
+ "@react-native/jest-preset": "0.85.0",
74
+ "@types/react": "^19.2.0",
75
+ "commitlint": "^21.0.2",
76
+ "del-cli": "^7.0.0",
77
+ "eslint": "^9.39.4",
78
+ "eslint-config-prettier": "^10.1.8",
79
+ "eslint-plugin-ft-flow": "^3.0.11",
80
+ "eslint-plugin-prettier": "^5.5.6",
81
+ "jest": "^29.7.0",
82
+ "lefthook": "^2.1.9",
83
+ "prettier": "^3.8.3",
84
+ "react": "19.2.3",
85
+ "react-native": "0.85.0",
86
+ "react-native-builder-bob": "^0.43.0",
87
+ "turbo": "^2.9.16",
88
+ "typescript": "^6.0.3"
89
+ },
90
+ "peerDependencies": {
91
+ "react": "*",
92
+ "react-native": "*"
93
+ },
94
+ "workspaces": [
95
+ "example"
96
+ ],
97
+ "packageManager": "yarn@4.11.0",
98
+ "react-native-builder-bob": {
99
+ "source": "src",
100
+ "output": "lib",
101
+ "targets": [
102
+ [
103
+ "module",
104
+ {
105
+ "esm": true
106
+ }
107
+ ],
108
+ [
109
+ "typescript",
110
+ {
111
+ "project": "tsconfig.build.json"
112
+ }
113
+ ]
114
+ ]
115
+ },
116
+ "codegenConfig": {
117
+ "name": "ParityBlurViewSpec",
118
+ "type": "all",
119
+ "jsSrcsDir": "src",
120
+ "android": {
121
+ "javaPackageName": "com.parityblur"
122
+ },
123
+ "ios": {
124
+ "components": {
125
+ "ParityBlurView": {
126
+ "className": "ParityBlurView"
127
+ }
128
+ }
129
+ }
130
+ },
131
+ "prettier": {
132
+ "quoteProps": "consistent",
133
+ "singleQuote": true,
134
+ "tabWidth": 2,
135
+ "trailingComma": "es5",
136
+ "useTabs": false
137
+ },
138
+ "jest": {
139
+ "preset": "@react-native/jest-preset",
140
+ "testEnvironmentOptions": {
141
+ "customExportConditions": [
142
+ "require",
143
+ "react-native",
144
+ "react-native-parity-blur-source"
145
+ ]
146
+ },
147
+ "modulePathIgnorePatterns": [
148
+ "<rootDir>/example/node_modules",
149
+ "<rootDir>/lib/"
150
+ ]
151
+ },
152
+ "commitlint": {
153
+ "extends": [
154
+ "@commitlint/config-conventional"
155
+ ]
156
+ },
157
+ "create-react-native-library": {
158
+ "type": "fabric-view",
159
+ "languages": "kotlin-objc",
160
+ "tools": [
161
+ "eslint",
162
+ "jest",
163
+ "lefthook"
164
+ ],
165
+ "version": "0.63.0"
166
+ }
167
+ }
@@ -0,0 +1,195 @@
1
+ import * as React from 'react';
2
+
3
+ import NativeParityBlurView, {
4
+ Commands as NativeCommands,
5
+ } from './ParityBlurViewNativeComponent';
6
+ import {
7
+ DEFAULT_BLUR_RADIUS,
8
+ DEFAULT_DOWNSAMPLE,
9
+ DEFAULT_MAX_FPS,
10
+ DEFAULT_MODE,
11
+ DEFAULT_OVERLAY_COLOR,
12
+ DEFAULT_QUALITY,
13
+ DEFAULT_SATURATION,
14
+ MAX_MAX_FPS,
15
+ MIN_MAX_FPS,
16
+ } from './defaults';
17
+ import { BlurPresets } from './presets';
18
+ import type { BlurDownsample, BlurViewProps, BlurViewRef } from './types';
19
+
20
+ declare const __DEV__: boolean;
21
+
22
+ const LOG_TAG = '[react-native-parity-blur]';
23
+
24
+ function clampBlurRadius(value: number | undefined): number {
25
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
26
+ return DEFAULT_BLUR_RADIUS;
27
+ }
28
+ return value < 0 ? 0 : value;
29
+ }
30
+
31
+ function clampSaturation(value: number | undefined): number {
32
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
33
+ return DEFAULT_SATURATION;
34
+ }
35
+ return value < 0 ? 0 : value;
36
+ }
37
+
38
+ function clampMaxFps(value: number | undefined): number {
39
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
40
+ return DEFAULT_MAX_FPS;
41
+ }
42
+ return Math.min(MAX_MAX_FPS, Math.max(MIN_MAX_FPS, value));
43
+ }
44
+
45
+ /**
46
+ * Normalizes the public `downsample` prop (`'auto' | 1 | 2 | 4 | 8`) into the
47
+ * Int32 the native Fabric prop accepts, where `0` is a sentinel for `'auto'`.
48
+ * See the deviation note in ParityBlurViewNativeComponent.ts for why this
49
+ * isn't a string/numeric enum.
50
+ */
51
+ function normalizeDownsample(
52
+ value: BlurDownsample | undefined
53
+ ): 0 | 1 | 2 | 4 | 8 {
54
+ if (value === undefined || value === 'auto') {
55
+ return 0;
56
+ }
57
+ if (value === 1 || value === 2 || value === 4 || value === 8) {
58
+ return value;
59
+ }
60
+ if (__DEV__) {
61
+ console.warn(
62
+ `${LOG_TAG} Invalid "downsample" value: ${JSON.stringify(
63
+ value
64
+ )}. Expected 'auto' | 1 | 2 | 4 | 8. Falling back to '${DEFAULT_DOWNSAMPLE}'.`
65
+ );
66
+ }
67
+ return 0;
68
+ }
69
+
70
+ type NativeInstance = React.ComponentRef<typeof NativeParityBlurView>;
71
+
72
+ /**
73
+ * Public backdrop blur component (plan §28/§29/§30).
74
+ *
75
+ * Renders the native ParityBlurView host view backed by a real blur on both
76
+ * platforms: Android API 31+ real-time `RenderEffect` Gaussian blur (older
77
+ * Android renders `fallbackColor` instead — real CPU blur is intentionally
78
+ * out of scope, plan §44), iOS `MPSImageGaussianBlur`. `refresh()` is fully
79
+ * wired to a native coalesced recapture (plan §29). See
80
+ * docs/PIPELINE_SPEC.md for the canonical pipeline both backends implement
81
+ * and docs/CALIBRATION_REPORT.md for measured cross-platform parity.
82
+ */
83
+ export const BlurView = React.forwardRef<BlurViewRef, BlurViewProps>(
84
+ function BlurView(props, ref) {
85
+ const {
86
+ preset,
87
+ blurRadius,
88
+ mode = DEFAULT_MODE,
89
+ overlayColor,
90
+ saturation,
91
+ quality = DEFAULT_QUALITY,
92
+ downsample,
93
+ maxFps,
94
+ fallbackColor,
95
+ // Accepted for public-API shape stability (plan §18); not yet wired
96
+ // to native capture logic. Deferred to v1.1 — see docs/LIMITATIONS.md.
97
+ blurTarget: _blurTarget,
98
+ children,
99
+ style,
100
+ } = props;
101
+
102
+ // A preset (plan §11) is pure JS sugar for a base
103
+ // { blurRadius, saturation, overlayColor } bundle — resolved here,
104
+ // client-side, with no native awareness of presets at all. Any of
105
+ // those three props passed explicitly above still wins per-prop.
106
+ const presetProps = preset ? BlurPresets[preset] : undefined;
107
+ const resolvedBlurRadius = blurRadius ?? presetProps?.blurRadius;
108
+ const resolvedSaturation = saturation ?? presetProps?.saturation;
109
+ const resolvedOverlayColor =
110
+ overlayColor ?? presetProps?.overlayColor ?? DEFAULT_OVERLAY_COLOR;
111
+
112
+ const nativeRef = React.useRef<NativeInstance>(null);
113
+
114
+ React.useImperativeHandle(
115
+ ref,
116
+ (): BlurViewRef => ({
117
+ refresh() {
118
+ if (nativeRef.current) {
119
+ NativeCommands.refresh(nativeRef.current);
120
+ }
121
+ },
122
+ }),
123
+ []
124
+ );
125
+
126
+ if (__DEV__) {
127
+ if (preset !== undefined && !(preset in BlurPresets)) {
128
+ console.warn(
129
+ `${LOG_TAG} Unknown "preset" value: ${JSON.stringify(preset)}. Ignoring.`
130
+ );
131
+ }
132
+ if (
133
+ blurRadius !== undefined &&
134
+ (typeof blurRadius !== 'number' ||
135
+ !Number.isFinite(blurRadius) ||
136
+ blurRadius < 0)
137
+ ) {
138
+ console.warn(
139
+ `${LOG_TAG} "blurRadius" must be a finite number >= 0. Received: ${blurRadius}. Clamping to ${clampBlurRadius(
140
+ blurRadius
141
+ )}.`
142
+ );
143
+ }
144
+ if (
145
+ saturation !== undefined &&
146
+ (typeof saturation !== 'number' ||
147
+ !Number.isFinite(saturation) ||
148
+ saturation < 0)
149
+ ) {
150
+ console.warn(
151
+ `${LOG_TAG} "saturation" must be a finite number >= 0. Received: ${saturation}. Clamping to ${clampSaturation(
152
+ saturation
153
+ )}.`
154
+ );
155
+ }
156
+ if (
157
+ maxFps !== undefined &&
158
+ (typeof maxFps !== 'number' ||
159
+ !Number.isFinite(maxFps) ||
160
+ maxFps < MIN_MAX_FPS ||
161
+ maxFps > MAX_MAX_FPS)
162
+ ) {
163
+ console.warn(
164
+ `${LOG_TAG} "maxFps" must be a finite number between ${MIN_MAX_FPS} and ${MAX_MAX_FPS}. Received: ${maxFps}. Clamping to ${clampMaxFps(
165
+ maxFps
166
+ )}.`
167
+ );
168
+ }
169
+ }
170
+
171
+ const safeBlurRadius = clampBlurRadius(resolvedBlurRadius);
172
+ const safeSaturation = clampSaturation(resolvedSaturation);
173
+ const safeMaxFps = clampMaxFps(maxFps);
174
+ const safeDownsample = normalizeDownsample(downsample);
175
+
176
+ return (
177
+ <NativeParityBlurView
178
+ ref={nativeRef}
179
+ style={style}
180
+ blurRadius={safeBlurRadius}
181
+ mode={mode}
182
+ overlayColor={resolvedOverlayColor}
183
+ saturation={safeSaturation}
184
+ quality={quality}
185
+ downsample={safeDownsample}
186
+ maxFps={safeMaxFps}
187
+ fallbackColor={fallbackColor}
188
+ >
189
+ {children}
190
+ </NativeParityBlurView>
191
+ );
192
+ }
193
+ );
194
+
195
+ export default BlurView;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Fabric codegen spec for the ParityBlurView native host view.
3
+ *
4
+ * Every public prop from plan §28 is plumbed through to native, and the
5
+ * `refresh()` Fabric command is wired end-to-end to a real coalesced
6
+ * recapture. Both native backends (Android Kotlin, iOS Swift) implement the
7
+ * real capture/blur/saturate/overlay pipeline defined in
8
+ * docs/PIPELINE_SPEC.md — this file only declares the codegen shape, not
9
+ * the behavior.
10
+ *
11
+ * Deviation from plan §28 (documented in the Milestone 1 report): the public
12
+ * `downsample` prop is `'auto' | 1 | 2 | 4 | 8` (see src/types.ts). Fabric
13
+ * codegen's TypeScript parser rejects a union that mixes string and numeric
14
+ * literals for one prop ("Mixed types are not supported"), and a plain
15
+ * string enum with numeric-looking values ('1', '2', ...) is also unsafe
16
+ * because the iOS/C++ generator turns each enum option into a bare
17
+ * identifier (`ParityBlurViewDownsample::1` is not valid C++). Instead this
18
+ * native prop is a plain Int32 where `0` is a sentinel for `'auto'` and
19
+ * `1 | 2 | 4 | 8` pass through unchanged. `src/BlurView.tsx` performs the
20
+ * 'auto' <-> 0 translation; native code treats 0 as "auto".
21
+ *
22
+ * Note: this file deliberately imports everything from the 'react-native'
23
+ * package root rather than deep 'react-native/Libraries/...' paths. This
24
+ * project's tsconfig opts into the `react-native-strict-api` custom
25
+ * condition, and react-native's package.json `exports` map explicitly
26
+ * blocks deep `Libraries/*` subpath imports under that condition -- so
27
+ * `codegenNativeCommands`, `codegenNativeComponent`, and the CodegenTypes
28
+ * (Double/Int32/WithDefault) must come from the root barrel, which
29
+ * re-exports all three for exactly this reason.
30
+ *
31
+ * Also note: do NOT re-export `CodegenTypes.WithDefault`/`Double`/`Int32` as
32
+ * local generic type aliases (an earlier draft of this file did, to shorten
33
+ * the qualified names). @react-native/codegen's alias resolution does plain
34
+ * textual substitution with no generic instantiation, so a chain like
35
+ * `NativeBlurMode -> WithDefault<'static'|'live','static'> -> (local
36
+ * `WithDefault<Type,Value>` alias) -> CodegenTypes.WithDefault<Type,Value>`
37
+ * loses the real type arguments and fails with "The default value in
38
+ * WithDefault must be string, number, boolean or null." Referencing
39
+ * `CodegenTypes.WithDefault<...>` directly (qualified) avoids the extra
40
+ * alias hop and keeps the real arguments intact.
41
+ */
42
+ import {
43
+ codegenNativeCommands,
44
+ codegenNativeComponent,
45
+ type CodegenTypes,
46
+ type ColorValue,
47
+ type HostComponent,
48
+ type ViewProps,
49
+ } from 'react-native';
50
+ import type * as React from 'react';
51
+
52
+ type NativeBlurMode = CodegenTypes.WithDefault<'static' | 'live', 'static'>;
53
+ type NativeBlurQuality = CodegenTypes.WithDefault<
54
+ 'high' | 'balanced' | 'performance',
55
+ 'balanced'
56
+ >;
57
+
58
+ export interface NativeProps extends ViewProps {
59
+ blurRadius?: CodegenTypes.WithDefault<CodegenTypes.Double, 0>;
60
+ mode?: NativeBlurMode;
61
+ overlayColor?: ColorValue;
62
+ saturation?: CodegenTypes.WithDefault<CodegenTypes.Double, 1>;
63
+ quality?: NativeBlurQuality;
64
+ // 0 = 'auto' sentinel; see file-level deviation note above.
65
+ downsample?: CodegenTypes.WithDefault<CodegenTypes.Int32, 0>;
66
+ maxFps?: CodegenTypes.WithDefault<CodegenTypes.Int32, 30>;
67
+ fallbackColor?: ColorValue;
68
+ }
69
+
70
+ type ComponentType = HostComponent<NativeProps>;
71
+
72
+ interface NativeCommands {
73
+ refresh: (viewRef: React.ComponentRef<ComponentType>) => void;
74
+ }
75
+
76
+ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
77
+ supportedCommands: ['refresh'],
78
+ });
79
+
80
+ export default codegenNativeComponent<NativeProps>(
81
+ 'ParityBlurView'
82
+ ) as ComponentType;
@@ -0,0 +1,20 @@
1
+ import type { BlurDownsample, BlurMode, BlurQuality } from './types';
2
+
3
+ export const DEFAULT_BLUR_RADIUS = 0;
4
+ export const DEFAULT_MODE: BlurMode = 'static';
5
+ export const DEFAULT_OVERLAY_COLOR = 'transparent';
6
+ export const DEFAULT_SATURATION = 1;
7
+ export const DEFAULT_QUALITY: BlurQuality = 'balanced';
8
+ export const DEFAULT_DOWNSAMPLE: BlurDownsample = 'auto';
9
+ export const DEFAULT_MAX_FPS = 30;
10
+
11
+ export const MIN_MAX_FPS = 1;
12
+ export const MAX_MAX_FPS = 120;
13
+
14
+ export const ALLOWED_DOWNSAMPLE_VALUES: ReadonlyArray<BlurDownsample> = [
15
+ 'auto',
16
+ 1,
17
+ 2,
18
+ 4,
19
+ 8,
20
+ ];