@capgo/capacitor-native-navigation 8.4.5 → 8.6.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.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  <a href="https://capgo.app/?ref=plugin_native_navigation"><img src="https://capgo.app/readme-banner.svg?repo=Cap-go/capacitor-native-navigation" alt="Capgo - Instant updates for Capacitor" /></a>
4
4
 
5
5
  <div align="center">
6
- <p><b>Capgo</b>: open-source live updates for Ionic and Capacitor apps. Ship OTA fixes and features instantly, without waiting for app store review.</p>
6
+ <p><b>Capgo</b>: push fixes to your Capacitor users in minutes, build signed iOS and Android apps without a Mac, and roll back in one click.</p>
7
7
  <h2><a href="https://capgo.app/register/?ref=plugin_native_navigation">➡️ Get started for free</a></h2>
8
8
  <p>14-day unlimited free trial. No credit card required</p>
9
9
  <p><a href="https://capgo.app/consulting/?ref=plugin_native_navigation">Missing a feature? We'll build the plugin for you 💪</a></p>
@@ -159,7 +159,7 @@ await NativeNavigation.setTabbar({
159
159
 
160
160
  ## Android Liquid Glass
161
161
 
162
- Enable `glass.effect: 'liquidGlass'` to draw a live blurred WebView backdrop behind Android native bars. Android 12+ uses a platform `RenderEffect` blur; older Android versions keep the translucent tint surface without live blur.
162
+ On Android 12+, floating tabbars use `liquidGlass` automatically when `setTabbar` omits `glass` (pass `glass: { effect: 'none' }` for a flat fill). The live blur uses a platform `RenderEffect` behind the bar shape; older Android versions keep a translucent tint without live blur. Tune `colors.background` alpha and `glass.surfaceAlpha` (default `0.62`) together.
163
163
 
164
164
  ```typescript
165
165
  await NativeNavigation.configure({
@@ -650,17 +650,17 @@ Global plugin configuration.
650
650
 
651
651
  Native bar colors. Use CSS-style hex strings (`#RRGGBB` or `#AARRGGBB`).
652
652
 
653
- | Prop | Type | Description |
654
- | --------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------- |
655
- | **`dynamic`** | <code>boolean</code> | When `true`, Android 12+ derives unspecified bar colors from Material You system palettes. Explicit color fields still win. |
656
- | **`tint`** | <code>string</code> | Tint color for active buttons/items. |
657
- | **`inactiveTint`** | <code>string</code> | Color for inactive tab items. Ignored on iOS 26+ unless `experimentalBakedTintColors` is enabled. |
658
- | **`background`** | <code>string</code> | Optional background tint. Ignored on iOS 26+ so UIKit can preserve the system Liquid Glass navigation appearance. |
659
- | **`foreground`** | <code>string</code> | Title and label text color where the native platform supports it. |
660
- | **`badgeBackground`** | <code>string</code> | Badge background color for native tab badges. |
661
- | **`badgeText`** | <code>string</code> | Badge text color for native tab badges. |
662
- | **`indicator`** | <code>string</code> | Active tab indicator color on Android. |
663
- | **`ripple`** | <code>string</code> | Tab press ripple color on Android. |
653
+ | Prop | Type | Description |
654
+ | --------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
655
+ | **`dynamic`** | <code>boolean</code> | When `true`, Android 12+ derives unspecified bar colors from Material You system palettes. Explicit color fields still win. |
656
+ | **`tint`** | <code>string</code> | Tint color for active buttons/items. |
657
+ | **`inactiveTint`** | <code>string</code> | Color for inactive tab items. Ignored on iOS 26+ unless `experimentalBakedTintColors` is enabled. |
658
+ | **`background`** | <code>string</code> | Optional background tint. Ignored on iOS 26+ so UIKit can preserve the system Liquid Glass navigation appearance. On Android, alpha is honored. When `glass.effect` is not `liquidGlass`, floating tabbars still apply `glass.surfaceAlpha` (from `configure` / `setTabbar`) to the fill tint. |
659
+ | **`foreground`** | <code>string</code> | Title and label text color where the native platform supports it. |
660
+ | **`badgeBackground`** | <code>string</code> | Badge background color for native tab badges. |
661
+ | **`badgeText`** | <code>string</code> | Badge text color for native tab badges. |
662
+ | **`indicator`** | <code>string</code> | Active tab indicator color on Android. |
663
+ | **`ripple`** | <code>string</code> | Tab press ripple color on Android. |
664
664
 
665
665
 
666
666
  #### NativeNavigationGlassOptions
@@ -736,26 +736,26 @@ because icons are rendered by native UI.
736
736
 
737
737
  Native tabbar state.
738
738
 
739
- | Prop | Type | Description |
740
- | ------------------------------------ | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
741
- | **`hidden`** | <code>boolean</code> | Hide the native tabbar. |
742
- | **`tabs`** | <code>NativeNavigationTab[]</code> | Tab definitions. |
743
- | **`selectedId`** | <code>string</code> | Currently selected tab id. |
744
- | **`labels`** | <code>boolean</code> | Show text labels. Defaults to `true`. |
745
- | **`labelVisibilityMode`** | <code><a href="#nativenavigationtablabelvisibilitymode">NativeNavigationTabLabelVisibilityMode</a></code> | Native label visibility mode. Overrides `labels` when provided. |
746
- | **`icons`** | <code>boolean</code> | Show icons. Defaults to `true`. |
747
- | **`colors`** | <code><a href="#nativenavigationcolors">NativeNavigationColors</a></code> | Tabbar color hints. |
748
- | **`blurEffect`** | <code><a href="#nativenavigationblureffect">NativeNavigationBlurEffect</a></code> | iOS blur/material effect for the tabbar background when glass is not available. |
749
- | **`glass`** | <code><a href="#nativenavigationglassoptions">NativeNavigationGlassOptions</a></code> | Optional glass background behavior. Overrides `configure({ glass })` for this tabbar update. |
750
- | **`experimentalBakedTintColors`** | <code>boolean</code> | Opt into the iOS 26 Liquid Glass tint workaround that renders active and inactive tab items into baked images. This can affect badge positioning and icon sizing, so it is disabled by default. |
751
- | **`disableTransparentOnScrollEdge`** | <code>boolean</code> | Keep the iOS scroll-edge tabbar appearance from becoming transparent. Mirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option. Defaults to `false`. |
752
- | **`disableIndicator`** | <code>boolean</code> | Disable the Android active tab indicator. |
753
- | **`indicatorColor`** | <code>string</code> | Active tab indicator color on Android. `colors.indicator` is also supported. |
754
- | **`rippleColor`** | <code>string</code> | Tab press ripple color on Android. `colors.ripple` is also supported. |
755
- | **`badgeBackgroundColor`** | <code>string</code> | Badge background color. `colors.badgeBackground` is also supported. |
756
- | **`badgeTextColor`** | <code>string</code> | Badge text color. `colors.badgeText` is also supported. |
757
- | **`style`** | <code><a href="#nativenavigationtabbarstyle">NativeNavigationTabbarStyle</a></code> | Optional native tabbar layout and shape customization. |
758
- | **`animated`** | <code>boolean</code> | Animate native tabbar changes. |
739
+ | Prop | Type | Description |
740
+ | ------------------------------------ | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
741
+ | **`hidden`** | <code>boolean</code> | Hide the native tabbar. |
742
+ | **`tabs`** | <code>NativeNavigationTab[]</code> | Tab definitions. |
743
+ | **`selectedId`** | <code>string</code> | Currently selected tab id. |
744
+ | **`labels`** | <code>boolean</code> | Show text labels. Defaults to `true`. |
745
+ | **`labelVisibilityMode`** | <code><a href="#nativenavigationtablabelvisibilitymode">NativeNavigationTabLabelVisibilityMode</a></code> | Native label visibility mode. Overrides `labels` when provided. |
746
+ | **`icons`** | <code>boolean</code> | Show icons. Defaults to `true`. |
747
+ | **`colors`** | <code><a href="#nativenavigationcolors">NativeNavigationColors</a></code> | Tabbar color hints. |
748
+ | **`blurEffect`** | <code><a href="#nativenavigationblureffect">NativeNavigationBlurEffect</a></code> | iOS blur/material effect for the tabbar background when glass is not available. |
749
+ | **`glass`** | <code><a href="#nativenavigationglassoptions">NativeNavigationGlassOptions</a></code> | Optional glass background behavior. Overrides `configure({ glass })` for this tabbar update. On Android 12+, floating tabbars default to `liquidGlass` (live blur plus tint) when this field is omitted. Pass `effect: 'none'` for a flat translucent fill without blur. |
750
+ | **`experimentalBakedTintColors`** | <code>boolean</code> | Opt into the iOS 26 Liquid Glass tint workaround that renders active and inactive tab items into baked images. This can affect badge positioning and icon sizing, so it is disabled by default. |
751
+ | **`disableTransparentOnScrollEdge`** | <code>boolean</code> | Keep the iOS scroll-edge tabbar appearance from becoming transparent. Mirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option. Defaults to `false`. |
752
+ | **`disableIndicator`** | <code>boolean</code> | Disable the Android active tab indicator. |
753
+ | **`indicatorColor`** | <code>string</code> | Active tab indicator color on Android. `colors.indicator` is also supported. |
754
+ | **`rippleColor`** | <code>string</code> | Tab press ripple color on Android. `colors.ripple` is also supported. |
755
+ | **`badgeBackgroundColor`** | <code>string</code> | Badge background color. `colors.badgeBackground` is also supported. |
756
+ | **`badgeTextColor`** | <code>string</code> | Badge text color. `colors.badgeText` is also supported. |
757
+ | **`style`** | <code><a href="#nativenavigationtabbarstyle">NativeNavigationTabbarStyle</a></code> | Optional native tabbar layout and shape customization. |
758
+ | **`animated`** | <code>boolean</code> | Animate native tabbar changes. |
759
759
 
760
760
 
761
761
  #### NativeNavigationTab
@@ -36,6 +36,7 @@ import androidx.annotation.RequiresApi;
36
36
  import androidx.appcompat.content.res.AppCompatResources;
37
37
  import androidx.appcompat.widget.Toolbar;
38
38
  import androidx.core.graphics.PathParser;
39
+ import androidx.core.widget.TextViewCompat;
39
40
  import com.getcapacitor.JSArray;
40
41
  import com.getcapacitor.JSObject;
41
42
  import com.getcapacitor.Plugin;
@@ -61,6 +62,11 @@ public class NativeNavigationPlugin extends Plugin {
61
62
 
62
63
  private static final int DEFAULT_NAVBAR_DP = 56;
63
64
  private static final int DEFAULT_TABBAR_DP = 64;
65
+ private static final int TAB_LABEL_TEXT_SP = 12;
66
+ private static final int TAB_CONTENT_PADDING_DP = 4;
67
+ private static final int INDICATOR_HORIZONTAL_PADDING_DP = 12;
68
+ private static final int INDICATOR_VERTICAL_PADDING_DP = 6;
69
+ private static final int INDICATOR_SLOT_INSET_DP = 2;
64
70
  private static final int DEFAULT_TRANSITION_MS = 350;
65
71
  private static final int MENU_ITEM_BASE = 10_000;
66
72
  private static final int DEFAULT_TABBAR_BACKGROUND_COLOR = Color.WHITE;
@@ -81,6 +87,7 @@ public class NativeNavigationPlugin extends Plugin {
81
87
  private boolean tabbarVisible = false;
82
88
  private String contentInsetMode = "css";
83
89
  private GlassOptions defaultGlassOptions = GlassOptions.defaults();
90
+ private boolean configureSpecifiedGlassEffect = false;
84
91
  private GlassOptions navbarGlassOptions = GlassOptions.defaults();
85
92
  private GlassOptions tabbarGlassOptions = GlassOptions.defaults();
86
93
  private JSObject navbarGlassConfig;
@@ -105,6 +112,7 @@ public class NativeNavigationPlugin extends Plugin {
105
112
  private final Map<Integer, String> menuActionPlacements = new HashMap<>();
106
113
  private final List<NativeTabItem> tabItems = new ArrayList<>();
107
114
  private int selectedTabIndex = 0;
115
+ private boolean tabbarChromeHideAnimating = false;
108
116
  private boolean tabbarDisableIndicator = false;
109
117
  private Integer tabbarIndicatorColor = null;
110
118
 
@@ -124,9 +132,11 @@ public class NativeNavigationPlugin extends Plugin {
124
132
  runOnUiThread(() -> {
125
133
  enabled = call.getBoolean("enabled", true);
126
134
  contentInsetMode = call.getString("contentInsetMode", contentInsetMode);
127
- defaultGlassOptions = GlassOptions.from(call.getObject("glass", null), defaultGlassOptions);
135
+ JSObject configureGlass = call.getObject("glass", null);
136
+ configureSpecifiedGlassEffect = configureGlass != null && configureGlass.has("effect");
137
+ defaultGlassOptions = GlassOptions.from(configureGlass, defaultGlassOptions);
128
138
  navbarGlassOptions = GlassOptions.from(navbarGlassConfig, defaultGlassOptions);
129
- tabbarGlassOptions = GlassOptions.from(tabbarGlassConfig, defaultGlassOptions);
139
+ tabbarGlassOptions = resolveTabbarGlassOptions(call);
130
140
  Double duration = call.getDouble("animationDuration");
131
141
  if (duration != null) {
132
142
  defaultTransitionMs = Math.max(0, duration.intValue());
@@ -218,17 +228,10 @@ public class NativeNavigationPlugin extends Plugin {
218
228
  }
219
229
 
220
230
  boolean hidden = call.getBoolean("hidden", false);
231
+ boolean animated = call.getBoolean("animated", false);
221
232
  tabbarVisible = !hidden;
222
233
  if (hidden) {
223
- if (tabbar != null) {
224
- tabbar.setVisibility(View.GONE);
225
- }
226
- if (tabbarContainer != null) {
227
- tabbarContainer.setVisibility(View.GONE);
228
- }
229
- if (tabbarBackdrop != null) {
230
- tabbarBackdrop.setVisibility(View.GONE);
231
- }
234
+ setTabbarChromeVisible(false, animated);
232
235
  updateInsetsAndNotify();
233
236
  call.resolve(insetsResult());
234
237
  return;
@@ -246,7 +249,6 @@ public class NativeNavigationPlugin extends Plugin {
246
249
  String selectedId = call.getString("selectedId", null);
247
250
  JSObject colors = call.getObject("colors", new JSObject());
248
251
  tabbarGlassConfig = call.getObject("glass", null);
249
- tabbarGlassOptions = GlassOptions.from(tabbarGlassConfig, defaultGlassOptions);
250
252
  badgeBackgroundColor = colorOption(call, colors, "badgeBackgroundColor", "badgeBackground", Color.rgb(255, 59, 48));
251
253
  badgeTextColor = colorOption(call, colors, "badgeTextColor", "badgeText", Color.WHITE);
252
254
  tabbarDisableIndicator = call.getBoolean("disableIndicator", false);
@@ -276,6 +278,7 @@ public class NativeNavigationPlugin extends Plugin {
276
278
 
277
279
  applyTabbarColors(call, colors);
278
280
  tabbarStyle = makeTabbarStyle(call.getObject("style", new JSObject()));
281
+ tabbarGlassOptions = resolveTabbarGlassOptions(call);
279
282
 
280
283
  // Keep at most one detached trailing action for floating bars.
281
284
  // Curve bars ignore role so tab order / center selection stay stable.
@@ -317,14 +320,8 @@ public class NativeNavigationPlugin extends Plugin {
317
320
 
318
321
  applyTabbarBackground(centerTabIndex());
319
322
  renderTabbarItems(labelVisibilityMode, icons);
320
- if (tabbarContainer != null) {
321
- tabbarContainer.setVisibility(View.VISIBLE);
322
- }
323
- if (tabbarBackdrop != null) {
324
- tabbarBackdrop.setVisibility(View.VISIBLE);
325
- }
326
- nativeTabbar.setVisibility(View.VISIBLE);
327
323
  layoutChrome();
324
+ setTabbarChromeVisible(true, animated);
328
325
  updateInsetsAndNotify();
329
326
  call.resolve(insetsResult());
330
327
  });
@@ -762,6 +759,10 @@ public class NativeNavigationPlugin extends Plugin {
762
759
  tabbarContainer.setClipChildren(false);
763
760
  tabbarContainer.setClipToPadding(false);
764
761
  tabbarContainer.setElevation(dp(12));
762
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
763
+ tabbarContainer.setOutlineAmbientShadowColor(0x33000000);
764
+ tabbarContainer.setOutlineSpotShadowColor(0x66000000);
765
+ }
765
766
 
766
767
  tabbarGlassBackdrop = new GlassBackdropView(getContext());
767
768
  tabbarGlassSurface = new View(getContext());
@@ -843,7 +844,7 @@ public class NativeNavigationPlugin extends Plugin {
843
844
  }
844
845
 
845
846
  private FrameLayout makeTabButton(NativeTabItem item, boolean selected, boolean showLabel, boolean icons, boolean center) {
846
- FrameLayout button = new FrameLayout(getContext());
847
+ TabButton button = new TabButton(getContext());
847
848
  button.setClipChildren(false);
848
849
  button.setClipToPadding(false);
849
850
  button.setForeground(selectableItemBackground());
@@ -857,25 +858,33 @@ public class NativeNavigationPlugin extends Plugin {
857
858
  button.addView(centerFill, new FrameLayout.LayoutParams(centerFillDiameter, centerFillDiameter, Gravity.CENTER));
858
859
  }
859
860
 
861
+ Drawable currentIcon = selected && item.selectedIcon != null ? item.selectedIcon : item.icon;
862
+ boolean labelVisible = center ? showLabel && (currentIcon == null || !icons) : showLabel;
863
+ boolean iconVisible = icons && currentIcon != null;
864
+ // Measured with the bold selected style for every tab so the slots don't shift when the selection changes.
865
+ int contentWidth = indicatorContentWidth(item.title, labelVisible, iconVisible);
866
+ button.preferredWidth = contentWidth + dp(INDICATOR_HORIZONTAL_PADDING_DP + INDICATOR_SLOT_INSET_DP) * 2;
867
+
860
868
  if (!center && selected && !tabbarDisableIndicator) {
861
869
  GradientDrawable selectedBackground = new GradientDrawable();
862
- selectedBackground.setShape(GradientDrawable.OVAL);
870
+ selectedBackground.setShape(GradientDrawable.RECTANGLE);
863
871
  selectedBackground.setColor(TabbarChromeSupport.resolveSelectedIndicatorColor(tabbarIndicatorColor, tintColor));
864
- View selectedCircle = new View(getContext());
865
- selectedCircle.setBackground(selectedBackground);
866
- button.addView(selectedCircle, new FrameLayout.LayoutParams(dp(58), dp(58), Gravity.CENTER));
872
+ TabIndicatorView indicator = new TabIndicatorView(getContext(), contentWidth, indicatorContentHeight(labelVisible, iconVisible));
873
+ indicator.setBackground(selectedBackground);
874
+ button.addView(
875
+ indicator,
876
+ new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER)
877
+ );
867
878
  }
868
879
 
869
880
  LinearLayout content = new LinearLayout(getContext());
870
881
  content.setOrientation(LinearLayout.VERTICAL);
871
882
  content.setGravity(Gravity.CENTER);
872
- content.setPadding(dp(4), dp(4), dp(4), dp(4));
883
+ content.setPadding(dp(TAB_CONTENT_PADDING_DP), dp(TAB_CONTENT_PADDING_DP), dp(TAB_CONTENT_PADDING_DP), dp(TAB_CONTENT_PADDING_DP));
873
884
  button.addView(content, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
874
885
 
875
- Drawable currentIcon = selected && item.selectedIcon != null ? item.selectedIcon : item.icon;
876
- boolean labelVisible = center ? showLabel && (currentIcon == null || !icons) : showLabel;
877
886
  int itemColor = center ? tabbarStyle.centerButtonIconColor : (selected ? tintColor : inactiveTintColor);
878
- if (icons && currentIcon != null) {
887
+ if (iconVisible) {
879
888
  Drawable icon = currentIcon.mutate();
880
889
  icon.setTint(itemColor);
881
890
  ImageView image = new ImageView(getContext());
@@ -891,10 +900,13 @@ public class NativeNavigationPlugin extends Plugin {
891
900
  TextView label = new TextView(getContext());
892
901
  label.setText(item.title);
893
902
  label.setTextColor(itemColor);
894
- label.setTextSize(12);
903
+ label.setTextSize(TAB_LABEL_TEXT_SP);
895
904
  label.setTypeface(Typeface.DEFAULT, selected ? Typeface.BOLD : Typeface.NORMAL);
896
905
  label.setGravity(Gravity.CENTER);
897
- label.setSingleLine(true);
906
+ // maxLines rather than singleLine: autosize is ignored on a single-line (horizontally scrolling) TextView.
907
+ label.setMaxLines(1);
908
+ // Shrink long titles to fit the tab instead of cutting them off.
909
+ TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(label, 9, TAB_LABEL_TEXT_SP, 1, TypedValue.COMPLEX_UNIT_SP);
898
910
  content.addView(label, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp(18)));
899
911
  }
900
912
 
@@ -924,6 +936,75 @@ public class NativeNavigationPlugin extends Plugin {
924
936
  return button;
925
937
  }
926
938
 
939
+ /** Width of the selected item's icon or label (whichever is wider), before the indicator padding. */
940
+ private int indicatorContentWidth(String title, boolean labelVisible, boolean iconVisible) {
941
+ int width = iconVisible ? dp(24) : 0;
942
+ if (labelVisible && title != null) {
943
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
944
+ paint.setTypeface(Typeface.DEFAULT_BOLD);
945
+ paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, TAB_LABEL_TEXT_SP, getContext().getResources().getDisplayMetrics()));
946
+ width = Math.max(width, (int) Math.ceil(paint.measureText(title)));
947
+ }
948
+ return width;
949
+ }
950
+
951
+ private int indicatorContentHeight(boolean labelVisible, boolean iconVisible) {
952
+ int height = 0;
953
+ if (iconVisible) {
954
+ height += dp(24);
955
+ }
956
+ if (labelVisible) {
957
+ height += dp(18) + (iconVisible ? dp(2) : 0);
958
+ }
959
+ return height;
960
+ }
961
+
962
+ /** Tab item that reports how wide it wants to be, so the tabbar can size slots to their content. */
963
+ private static final class TabButton extends FrameLayout {
964
+
965
+ int preferredWidth = 0;
966
+
967
+ TabButton(Context context) {
968
+ super(context);
969
+ }
970
+ }
971
+
972
+ /** Selected-tab capsule that wraps the item's icon and label with padding, kept inside its tab slot. */
973
+ private static final class TabIndicatorView extends View {
974
+
975
+ private final int contentWidth;
976
+ private final int contentHeight;
977
+
978
+ TabIndicatorView(Context context, int contentWidth, int contentHeight) {
979
+ super(context);
980
+ this.contentWidth = contentWidth;
981
+ this.contentHeight = contentHeight;
982
+ }
983
+
984
+ @Override
985
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
986
+ int slotWidth = MeasureSpec.getSize(widthMeasureSpec);
987
+ int slotHeight = MeasureSpec.getSize(heightMeasureSpec);
988
+ int height = contentHeight + dp(INDICATOR_VERTICAL_PADDING_DP) * 2;
989
+ if (slotHeight > 0) {
990
+ height = Math.min(height, slotHeight);
991
+ }
992
+ int width = Math.max(height, contentWidth + dp(INDICATOR_HORIZONTAL_PADDING_DP) * 2);
993
+ if (slotWidth > 0) {
994
+ width = Math.min(width, slotWidth - dp(INDICATOR_SLOT_INSET_DP) * 2);
995
+ }
996
+ setMeasuredDimension(width, height);
997
+ Drawable background = getBackground();
998
+ if (background instanceof GradientDrawable) {
999
+ ((GradientDrawable) background).setCornerRadius(height / 2f);
1000
+ }
1001
+ }
1002
+
1003
+ private int dp(int value) {
1004
+ return Math.round(value * getResources().getDisplayMetrics().density);
1005
+ }
1006
+ }
1007
+
927
1008
  private Drawable iconFrom(JSONObject descriptor) {
928
1009
  if (descriptor == null) {
929
1010
  return null;
@@ -1218,11 +1299,7 @@ public class NativeNavigationPlugin extends Plugin {
1218
1299
  return;
1219
1300
  }
1220
1301
 
1221
- int count = Math.max(getChildCount(), 1);
1222
- int childWidth = width / count;
1223
- for (int index = 0; index < getChildCount(); index++) {
1224
- measureChildExact(getChildAt(index), childWidth, height);
1225
- }
1302
+ measureRange(0, getChildCount(), width, height);
1226
1303
  setMeasuredDimension(width, height);
1227
1304
  }
1228
1305
 
@@ -1285,28 +1362,56 @@ public class NativeNavigationPlugin extends Plugin {
1285
1362
  }
1286
1363
 
1287
1364
  private void measureRange(int start, int end, int width, int height) {
1288
- int count = Math.max(0, end - start);
1289
- if (count == 0) {
1290
- return;
1291
- }
1292
- int childWidth = Math.max(0, width / count);
1365
+ int[] widths = slotWidths(start, end, width);
1293
1366
  for (int index = start; index < end; index++) {
1294
- measureChildExact(getChildAt(index), childWidth, height);
1367
+ measureChildExact(getChildAt(index), widths[index - start], height);
1295
1368
  }
1296
1369
  }
1297
1370
 
1298
1371
  private void layoutRange(int start, int end, int left, int top, int width, int height) {
1299
- int count = Math.max(0, end - start);
1300
- if (count == 0) {
1301
- return;
1302
- }
1303
- int childWidth = Math.max(0, width / count);
1372
+ int[] widths = slotWidths(start, end, width);
1373
+ int childLeft = left;
1304
1374
  for (int index = start; index < end; index++) {
1305
- int childLeft = left + (index - start) * childWidth;
1375
+ int childWidth = widths[index - start];
1306
1376
  getChildAt(index).layout(childLeft, top, childLeft + childWidth, top + height);
1377
+ childLeft += childWidth;
1307
1378
  }
1308
1379
  }
1309
1380
 
1381
+ /**
1382
+ * Each tab gets the width its content needs; leftover space is shared equally so the gaps stay even. When the
1383
+ * bar is too narrow, slots shrink in proportion and long labels autosize down.
1384
+ */
1385
+ private int[] slotWidths(int start, int end, int width) {
1386
+ int count = Math.max(0, end - start);
1387
+ int[] widths = new int[count];
1388
+ if (count == 0) {
1389
+ return widths;
1390
+ }
1391
+ int total = 0;
1392
+ for (int index = 0; index < count; index++) {
1393
+ View child = getChildAt(start + index);
1394
+ int preferred = child instanceof TabButton ? ((TabButton) child).preferredWidth : 0;
1395
+ widths[index] = Math.max(preferred, 0);
1396
+ total += widths[index];
1397
+ }
1398
+ if (total <= 0) {
1399
+ for (int index = 0; index < count; index++) {
1400
+ widths[index] = width / count;
1401
+ }
1402
+ } else if (total <= width) {
1403
+ int extra = (width - total) / count;
1404
+ for (int index = 0; index < count; index++) {
1405
+ widths[index] += extra;
1406
+ }
1407
+ } else {
1408
+ for (int index = 0; index < count; index++) {
1409
+ widths[index] = (int) ((long) widths[index] * width / total);
1410
+ }
1411
+ }
1412
+ return widths;
1413
+ }
1414
+
1310
1415
  private void measureChildExact(View child, int width, int height) {
1311
1416
  child.measure(
1312
1417
  MeasureSpec.makeMeasureSpec(Math.max(0, width), MeasureSpec.EXACTLY),
@@ -1650,11 +1755,14 @@ public class NativeNavigationPlugin extends Plugin {
1650
1755
  if (tabbarContainer != null) {
1651
1756
  tabbarContainer.setBackgroundColor(Color.TRANSPARENT);
1652
1757
  }
1653
- hideGlassBackground(null, tabbarGlassSurface);
1758
+ if (tabbarGlassSurface != null) {
1759
+ tabbarGlassSurface.setBackground(chromeBackgroundDrawable(drawColor, 0f));
1760
+ tabbarGlassSurface.setVisibility(View.VISIBLE);
1761
+ }
1654
1762
  if (tabbarGlassBackdrop != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
1655
1763
  View webView = getBridge() == null ? null : getBridge().getWebView();
1656
1764
  tabbarGlassBackdrop.setClipPathProvider((width, height) -> tabbar.backgroundPath(width, height));
1657
- tabbarGlassBackdrop.configure(webView, dp(resolvedGlassOptions.blurRadiusDp), drawColor);
1765
+ tabbarGlassBackdrop.configure(webView, dp(resolvedGlassOptions.blurRadiusDp), Color.TRANSPARENT);
1658
1766
  tabbarGlassBackdrop.setVisibility(View.VISIBLE);
1659
1767
  } else if (tabbarGlassBackdrop != null) {
1660
1768
  tabbarGlassBackdrop.setClipPathProvider((width, height) -> tabbar.backgroundPath(width, height));
@@ -1671,15 +1779,36 @@ public class NativeNavigationPlugin extends Plugin {
1671
1779
  }
1672
1780
  }
1673
1781
 
1674
- tabbar.setTabbarStyle(tabbarStyle, drawColor, centerIndex);
1782
+ boolean glassBlurActive =
1783
+ resolvedGlassOptions.isLiquidGlass() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
1784
+ int tabbarFillColor = glassBlurActive ? Color.TRANSPARENT : drawColor;
1785
+ tabbar.setTabbarStyle(tabbarStyle, tabbarFillColor, centerIndex);
1675
1786
  applyTabbarContainerOutline();
1676
1787
  }
1677
1788
 
1789
+ private GlassOptions resolveTabbarGlassOptions(PluginCall call) {
1790
+ if (tabbarGlassConfig != null) {
1791
+ return GlassOptions.from(tabbarGlassConfig, defaultGlassOptions);
1792
+ }
1793
+ boolean apiAtLeastS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
1794
+ if (
1795
+ TabbarChromeSupport.shouldEnableDefaultLiquidGlass(apiAtLeastS, tabbarStyle.isCurve(), false)
1796
+ && (!configureSpecifiedGlassEffect || defaultGlassOptions.isLiquidGlass())
1797
+ ) {
1798
+ return GlassOptions.from(new JSObject().put("effect", "liquidGlass"), defaultGlassOptions);
1799
+ }
1800
+ return GlassOptions.from(null, defaultGlassOptions);
1801
+ }
1802
+
1678
1803
  private int resolvedTabbarSurfaceColor() {
1679
1804
  GlassOptions resolvedGlassOptions = tabbarGlassOptions == null ? GlassOptions.defaults() : tabbarGlassOptions;
1680
- return resolvedGlassOptions.isLiquidGlass()
1681
- ? glassSurfaceColor(tabbarBackgroundColor, resolvedGlassOptions)
1682
- : tabbarBackgroundColor;
1805
+ if (resolvedGlassOptions.isLiquidGlass()) {
1806
+ return glassSurfaceColor(tabbarBackgroundColor, resolvedGlassOptions);
1807
+ }
1808
+ if (!tabbarStyle.isCurve()) {
1809
+ return glassSurfaceColor(tabbarBackgroundColor, resolvedGlassOptions);
1810
+ }
1811
+ return tabbarBackgroundColor;
1683
1812
  }
1684
1813
 
1685
1814
  private void reapplyVisibleChromeBackgrounds() {
@@ -1834,7 +1963,8 @@ public class NativeNavigationPlugin extends Plugin {
1834
1963
  int bottom = navigationBarInset();
1835
1964
  int navbarHeight = navbarVisible ? status + dp(DEFAULT_NAVBAR_DP) : 0;
1836
1965
  int tabbarHeight = dp(tabbarStyle.totalHeight());
1837
- int tabbarBottomMargin = tabbarVisible ? bottom + dp(tabbarStyle.bottomGap) : bottom;
1966
+ boolean tabbarLayoutVisible = tabbarVisible || tabbarChromeHideAnimating;
1967
+ int tabbarBottomMargin = tabbarLayoutVisible ? bottom + dp(tabbarStyle.bottomGap) : bottom;
1838
1968
 
1839
1969
  if (navbarContainer != null) {
1840
1970
  FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams(
@@ -1857,7 +1987,7 @@ public class NativeNavigationPlugin extends Plugin {
1857
1987
  if (tabbarBackdrop != null) {
1858
1988
  // Only the edge-to-edge curve bar extends its surface under the gesture area. A floating
1859
1989
  // tabbar must leave the page visible below it, or it sits on a solid band.
1860
- int backdropHeight = tabbarVisible && tabbarStyle.isCurve() ? bottom + dp(tabbarStyle.bottomGap) : 0;
1990
+ int backdropHeight = tabbarLayoutVisible && tabbarStyle.isCurve() ? bottom + dp(tabbarStyle.bottomGap) : 0;
1861
1991
  FrameLayout.LayoutParams backdropParams = new FrameLayout.LayoutParams(
1862
1992
  ViewGroup.LayoutParams.MATCH_PARENT,
1863
1993
  backdropHeight,
@@ -1865,7 +1995,7 @@ public class NativeNavigationPlugin extends Plugin {
1865
1995
  );
1866
1996
  tabbarBackdrop.setLayoutParams(backdropParams);
1867
1997
  tabbarBackdrop.setBackgroundColor(resolvedTabbarSurfaceColor());
1868
- tabbarBackdrop.setVisibility(tabbarVisible && backdropHeight > 0 ? View.VISIBLE : View.GONE);
1998
+ tabbarBackdrop.setVisibility(tabbarLayoutVisible && backdropHeight > 0 ? View.VISIBLE : View.GONE);
1869
1999
  }
1870
2000
  if (tabbarContainer != null) {
1871
2001
  int rootWidth = root.getWidth() > 0 ? root.getWidth() : Resources.getSystem().getDisplayMetrics().widthPixels;
@@ -1907,6 +2037,97 @@ public class NativeNavigationPlugin extends Plugin {
1907
2037
  bringChromeToFront();
1908
2038
  }
1909
2039
 
2040
+ private void setTabbarChromeVisible(boolean visible, boolean animated) {
2041
+ if (!animated) {
2042
+ tabbarChromeHideAnimating = false;
2043
+ int visibility = visible ? View.VISIBLE : View.GONE;
2044
+ if (tabbar != null) {
2045
+ tabbar.setVisibility(visibility);
2046
+ }
2047
+ if (tabbarContainer != null) {
2048
+ tabbarContainer.animate().cancel();
2049
+ tabbarContainer.setTranslationY(0f);
2050
+ tabbarContainer.setAlpha(1f);
2051
+ tabbarContainer.setVisibility(visibility);
2052
+ }
2053
+ if (tabbarBackdrop != null) {
2054
+ tabbarBackdrop.animate().cancel();
2055
+ tabbarBackdrop.setAlpha(1f);
2056
+ tabbarBackdrop.setVisibility(visible && tabbarVisible ? View.VISIBLE : View.GONE);
2057
+ }
2058
+ return;
2059
+ }
2060
+
2061
+ if (tabbarContainer == null) {
2062
+ setTabbarChromeVisible(visible, false);
2063
+ return;
2064
+ }
2065
+
2066
+ float slideDistance = tabbarSlideDistance();
2067
+ tabbarContainer.animate().cancel();
2068
+ if (tabbarBackdrop != null) {
2069
+ tabbarBackdrop.animate().cancel();
2070
+ }
2071
+
2072
+ if (!visible) {
2073
+ tabbarChromeHideAnimating = true;
2074
+ tabbarContainer
2075
+ .animate()
2076
+ .translationY(slideDistance)
2077
+ .alpha(0f)
2078
+ .setDuration(TabbarChromeSupport.TABBAR_VISIBILITY_ANIMATION_MS)
2079
+ .withEndAction(() -> {
2080
+ tabbarChromeHideAnimating = false;
2081
+ if (tabbar != null) {
2082
+ tabbar.setVisibility(View.GONE);
2083
+ }
2084
+ tabbarContainer.setVisibility(View.GONE);
2085
+ if (tabbarBackdrop != null) {
2086
+ tabbarBackdrop.setVisibility(View.GONE);
2087
+ }
2088
+ })
2089
+ .start();
2090
+ if (tabbarBackdrop != null) {
2091
+ tabbarBackdrop.animate().alpha(0f).setDuration(TabbarChromeSupport.TABBAR_VISIBILITY_ANIMATION_MS).start();
2092
+ }
2093
+ return;
2094
+ }
2095
+
2096
+ tabbarChromeHideAnimating = false;
2097
+ tabbarContainer.setVisibility(View.VISIBLE);
2098
+ tabbarContainer.setAlpha(0f);
2099
+ tabbarContainer.setTranslationY(slideDistance);
2100
+ if (tabbarBackdrop != null) {
2101
+ tabbarBackdrop.setVisibility(View.VISIBLE);
2102
+ tabbarBackdrop.setAlpha(0f);
2103
+ }
2104
+ if (tabbar != null) {
2105
+ tabbar.setVisibility(View.VISIBLE);
2106
+ }
2107
+ tabbarContainer
2108
+ .animate()
2109
+ .translationY(0f)
2110
+ .alpha(1f)
2111
+ .setDuration(TabbarChromeSupport.TABBAR_VISIBILITY_ANIMATION_MS)
2112
+ .start();
2113
+ if (tabbarBackdrop != null) {
2114
+ tabbarBackdrop.animate().alpha(1f).setDuration(TabbarChromeSupport.TABBAR_VISIBILITY_ANIMATION_MS).start();
2115
+ }
2116
+ }
2117
+
2118
+ private float tabbarSlideDistance() {
2119
+ if (tabbarContainer == null) {
2120
+ return dp(tabbarStyle.totalHeight() + tabbarStyle.bottomGap);
2121
+ }
2122
+ ViewGroup.LayoutParams params = tabbarContainer.getLayoutParams();
2123
+ int bottomMargin = params instanceof ViewGroup.MarginLayoutParams ? ((ViewGroup.MarginLayoutParams) params).bottomMargin : 0;
2124
+ return TabbarChromeSupport.tabbarSlideDistancePx(
2125
+ tabbarContainer.getHeight(),
2126
+ bottomMargin,
2127
+ dp(tabbarStyle.totalHeight() + tabbarStyle.bottomGap)
2128
+ );
2129
+ }
2130
+
1910
2131
  private void bringChromeToFront() {
1911
2132
  if (navbarContainer != null) {
1912
2133
  navbarContainer.bringToFront();
@@ -2079,26 +2300,37 @@ public class NativeNavigationPlugin extends Plugin {
2079
2300
  return Math.max(0, rawNavigationBarInset() - contentRootBottomOffset());
2080
2301
  }
2081
2302
 
2303
+ // API 30+ reads the currently visible bars so a hidden navigation bar (immersive mode) gives the space back.
2082
2304
  private int rawStatusBarInset() {
2083
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
2084
- WindowInsets insets = getActivity().getWindow().getDecorView().getRootWindowInsets();
2085
- if (insets != null) {
2086
- return insets.getStableInsetTop();
2305
+ WindowInsets insets = rootWindowInsets();
2306
+ if (insets != null) {
2307
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
2308
+ return insets.getInsets(WindowInsets.Type.statusBars() | WindowInsets.Type.displayCutout()).top;
2087
2309
  }
2310
+ return insets.getStableInsetTop();
2088
2311
  }
2089
2312
  return systemDimension("status_bar_height");
2090
2313
  }
2091
2314
 
2092
2315
  private int rawNavigationBarInset() {
2093
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
2094
- WindowInsets insets = getActivity().getWindow().getDecorView().getRootWindowInsets();
2095
- if (insets != null) {
2096
- return insets.getStableInsetBottom();
2316
+ WindowInsets insets = rootWindowInsets();
2317
+ if (insets != null) {
2318
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
2319
+ return insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
2097
2320
  }
2321
+ return insets.getStableInsetBottom();
2098
2322
  }
2099
2323
  return systemDimension("navigation_bar_height");
2100
2324
  }
2101
2325
 
2326
+ private WindowInsets rootWindowInsets() {
2327
+ Activity activity = getActivity();
2328
+ if (activity == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
2329
+ return null;
2330
+ }
2331
+ return activity.getWindow().getDecorView().getRootWindowInsets();
2332
+ }
2333
+
2102
2334
  private int contentRootTopOffset() {
2103
2335
  FrameLayout root = contentRoot();
2104
2336
  if (root == null || !root.isLaidOut()) {
@@ -2136,6 +2368,16 @@ public class NativeNavigationPlugin extends Plugin {
2136
2368
  view.post(this::updateInsetsAndNotify);
2137
2369
  }
2138
2370
  });
2371
+ // Showing or hiding a system bar changes the insets without necessarily moving the content root
2372
+ // (edge-to-edge apps). A zero-size probe hears every inset dispatch without replacing anyone's listener.
2373
+ View insetsProbe = new View(getContext());
2374
+ insetsProbe.setOnApplyWindowInsetsListener((view, insets) -> {
2375
+ if (navbarVisible || tabbarVisible) {
2376
+ view.post(this::updateInsetsAndNotify);
2377
+ }
2378
+ return insets;
2379
+ });
2380
+ root.addView(insetsProbe, new FrameLayout.LayoutParams(0, 0));
2139
2381
  }
2140
2382
 
2141
2383
  private int systemDimension(String name) {
@@ -6,6 +6,8 @@ import android.os.Build;
6
6
 
7
7
  public final class TabbarChromeSupport {
8
8
 
9
+ public static final int TABBAR_VISIBILITY_ANIMATION_MS = 200;
10
+
9
11
  public enum TabbarOutlineMode {
10
12
  FLOATING_SHAPE,
11
13
  CURVE_DEFAULT
@@ -13,6 +15,13 @@ public final class TabbarChromeSupport {
13
15
 
14
16
  private TabbarChromeSupport() {}
15
17
 
18
+ public static float tabbarSlideDistancePx(int containerHeight, int bottomMargin, int fallbackDistance) {
19
+ if (containerHeight > 0) {
20
+ return (float) containerHeight + (float) bottomMargin;
21
+ }
22
+ return (float) fallbackDistance;
23
+ }
24
+
16
25
  public static int resolveSelectedIndicatorColor(Integer explicitColor, int tintColor) {
17
26
  if (explicitColor != null) {
18
27
  return explicitColor;
@@ -28,6 +37,10 @@ public final class TabbarChromeSupport {
28
37
  return mode == TabbarOutlineMode.FLOATING_SHAPE;
29
38
  }
30
39
 
40
+ public static boolean shouldEnableDefaultLiquidGlass(boolean apiAtLeastS, boolean isCurve, boolean glassSpecified) {
41
+ return !glassSpecified && !isCurve && apiAtLeastS;
42
+ }
43
+
31
44
  public static int floatingCapsuleWidth(int totalWidth, int barHeightPx, int trailingGapPx, boolean hasDetachedTrailing) {
32
45
  if (!hasDetachedTrailing) {
33
46
  return totalWidth;
package/dist/docs.json CHANGED
@@ -419,7 +419,7 @@
419
419
  {
420
420
  "name": "background",
421
421
  "tags": [],
422
- "docs": "Optional background tint. Ignored on iOS 26+ so UIKit can preserve the\nsystem Liquid Glass navigation appearance.",
422
+ "docs": "Optional background tint. Ignored on iOS 26+ so UIKit can preserve the\nsystem Liquid Glass navigation appearance. On Android, alpha is honored.\nWhen `glass.effect` is not `liquidGlass`, floating tabbars still apply\n`glass.surfaceAlpha` (from `configure` / `setTabbar`) to the fill tint.",
423
423
  "complexTypes": [],
424
424
  "type": "string | undefined"
425
425
  },
@@ -791,7 +791,7 @@
791
791
  {
792
792
  "name": "glass",
793
793
  "tags": [],
794
- "docs": "Optional glass background behavior. Overrides `configure({ glass })` for\nthis tabbar update.",
794
+ "docs": "Optional glass background behavior. Overrides `configure({ glass })` for\nthis tabbar update. On Android 12+, floating tabbars default to\n`liquidGlass` (live blur plus tint) when this field is omitted. Pass\n`effect: 'none'` for a flat translucent fill without blur.",
795
795
  "complexTypes": [
796
796
  "NativeNavigationGlassOptions"
797
797
  ],
@@ -150,7 +150,9 @@ export interface NativeNavigationColors {
150
150
  inactiveTint?: string;
151
151
  /**
152
152
  * Optional background tint. Ignored on iOS 26+ so UIKit can preserve the
153
- * system Liquid Glass navigation appearance.
153
+ * system Liquid Glass navigation appearance. On Android, alpha is honored.
154
+ * When `glass.effect` is not `liquidGlass`, floating tabbars still apply
155
+ * `glass.surfaceAlpha` (from `configure` / `setTabbar`) to the fill tint.
154
156
  */
155
157
  background?: string;
156
158
  /**
@@ -439,7 +441,9 @@ export interface NativeNavigationTabbarOptions {
439
441
  blurEffect?: NativeNavigationBlurEffect;
440
442
  /**
441
443
  * Optional glass background behavior. Overrides `configure({ glass })` for
442
- * this tabbar update.
444
+ * this tabbar update. On Android 12+, floating tabbars default to
445
+ * `liquidGlass` (live blur plus tint) when this field is omitted. Pass
446
+ * `effect: 'none'` for a flat translucent fill without blur.
443
447
  */
444
448
  glass?: NativeNavigationGlassOptions;
445
449
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * Platform rendering preference for the native bars.\n */\nexport type NativeNavigationPlatformStyle = 'auto' | 'ios' | 'android';\n\n/**\n * How the plugin exposes native bar sizes to web content.\n */\nexport type NativeNavigationContentInsetMode = 'css' | 'none';\n\n/**\n * Navigation animation direction.\n */\nexport type NativeNavigationTransitionDirection = 'forward' | 'back' | 'root' | 'tab' | 'zoom' | 'none';\n\n/**\n * Native material/blur effect preference.\n */\nexport type NativeNavigationBlurEffect =\n | 'none'\n | 'systemDefault'\n | 'extraLight'\n | 'light'\n | 'dark'\n | 'regular'\n | 'prominent'\n | 'systemUltraThinMaterial'\n | 'systemThinMaterial'\n | 'systemMaterial'\n | 'systemThickMaterial'\n | 'systemChromeMaterial'\n | 'systemUltraThinMaterialLight'\n | 'systemThinMaterialLight'\n | 'systemMaterialLight'\n | 'systemThickMaterialLight'\n | 'systemChromeMaterialLight'\n | 'systemUltraThinMaterialDark'\n | 'systemThinMaterialDark'\n | 'systemMaterialDark'\n | 'systemThickMaterialDark'\n | 'systemChromeMaterialDark';\n\n/**\n * Native glass background rendering preference.\n */\nexport type NativeNavigationGlassEffect = 'none' | 'liquidGlass';\n\n/**\n * Native glass background configuration.\n */\nexport interface NativeNavigationGlassOptions {\n /**\n * `liquidGlass` enables the Android 12+ live blurred WebView backdrop for\n * native bars. Android 11 and older keep a translucent surface fallback. iOS\n * uses the platform-owned Liquid Glass behavior.\n */\n effect?: NativeNavigationGlassEffect;\n\n /**\n * Android blur radius in native dp for `liquidGlass`. Defaults to `18`.\n */\n blurRadius?: number;\n\n /**\n * Alpha multiplier for the tint surface over the glass backdrop. Defaults to\n * `0.62`.\n */\n surfaceAlpha?: number;\n}\n\n/**\n * Native tab label visibility behavior.\n */\nexport type NativeNavigationTabLabelVisibilityMode = 'auto' | 'selected' | 'labeled' | 'unlabeled';\n\n/**\n * A rectangle in WebView viewport coordinates, expressed in native points/dp.\n */\nexport interface NativeNavigationRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Native tabbar background shape.\n */\nexport type NativeNavigationTabbarShape = 'floating' | 'curve';\n\n/**\n * Native tab role for Liquid Glass tab bars.\n *\n * `search` (and `prominent` when the OS supports it) renders as a detached\n * trailing circular action beside the floating tab capsule — the Apple News /\n * Photos pattern. Only one detached trailing role is used; if multiple tabs\n * set `search` or `prominent`, the last one wins. Curve-shaped bars ignore\n * `role` and keep using the included center action instead.\n */\nexport type NativeNavigationTabRole = 'normal' | 'search' | 'prominent';\n\n/**\n * A serializable icon descriptor. Framework nodes are intentionally not accepted\n * because icons are rendered by native UI.\n */\nexport interface NativeNavigationIcon {\n /**\n * Cross-platform asset path or URL fallback.\n */\n src?: string;\n\n /**\n * Cross-platform inline SVG markup. The native renderers support common icon\n * shapes such as path, line, polyline, polygon, circle, and rect. SVG icons\n * are rendered as template images by default so native tint colors still\n * apply.\n */\n svg?: string;\n\n /**\n * Preferred rendered icon width in native points/dp. Defaults to `24`.\n */\n width?: number;\n\n /**\n * Preferred rendered icon height in native points/dp. Defaults to `24`.\n */\n height?: number;\n\n /**\n * When `true`, native tint colors are applied to the rendered SVG/image.\n * Defaults to `true`.\n */\n template?: boolean;\n\n /**\n * iOS-specific SF Symbol, bundled image name, or inline SVG.\n */\n ios?: {\n /**\n * SF Symbol name, for example `house.fill`.\n */\n sfSymbol?: string;\n\n /**\n * Bundled image name from the app asset catalog.\n */\n image?: string;\n\n /**\n * iOS-specific inline SVG markup.\n */\n svg?: string;\n };\n\n /**\n * Android-specific drawable resource, asset name, or inline SVG.\n */\n android?: {\n /**\n * Drawable resource name without the `R.drawable.` prefix.\n */\n resource?: string;\n\n /**\n * Bundled image asset name.\n */\n image?: string;\n\n /**\n * Android-specific inline SVG markup.\n */\n svg?: string;\n };\n}\n\n/**\n * Native bar colors. Use CSS-style hex strings (`#RRGGBB` or `#AARRGGBB`).\n */\nexport interface NativeNavigationColors {\n /**\n * When `true`, Android 12+ derives unspecified bar colors from Material You\n * system palettes. Explicit color fields still win.\n */\n dynamic?: boolean;\n\n /**\n * Tint color for active buttons/items.\n */\n tint?: string;\n\n /**\n * Color for inactive tab items. Ignored on iOS 26+ unless\n * `experimentalBakedTintColors` is enabled.\n */\n inactiveTint?: string;\n\n /**\n * Optional background tint. Ignored on iOS 26+ so UIKit can preserve the\n * system Liquid Glass navigation appearance.\n */\n background?: string;\n\n /**\n * Title and label text color where the native platform supports it.\n */\n foreground?: string;\n\n /**\n * Badge background color for native tab badges.\n */\n badgeBackground?: string;\n\n /**\n * Badge text color for native tab badges.\n */\n badgeText?: string;\n\n /**\n * Active tab indicator color on Android.\n */\n indicator?: string;\n\n /**\n * Tab press ripple color on Android.\n */\n ripple?: string;\n}\n\n/**\n * Global plugin configuration.\n */\nexport interface NativeNavigationConfigureOptions {\n /**\n * Enables or disables the native chrome host.\n */\n enabled?: boolean;\n\n /**\n * Native style preference. `auto` uses the current platform.\n */\n platformStyle?: NativeNavigationPlatformStyle;\n\n /**\n * When `css`, the plugin writes CSS variables on `document.documentElement`.\n */\n contentInsetMode?: NativeNavigationContentInsetMode;\n\n /**\n * Default native transition duration in milliseconds.\n */\n animationDuration?: number;\n\n /**\n * Shared color hints for native bars.\n */\n colors?: NativeNavigationColors;\n\n /**\n * Shared glass background defaults for native bars.\n */\n glass?: NativeNavigationGlassOptions;\n}\n\n/**\n * A button shown in the native navbar.\n */\nexport interface NativeNavigationBarButton {\n /**\n * Stable id returned in `navbarItemTap`.\n */\n id: string;\n\n /**\n * Visible text label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Whether the action is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n/**\n * Native back button configuration.\n */\nexport interface NativeNavigationBackButton {\n /**\n * Show the native back affordance.\n */\n visible?: boolean;\n\n /**\n * Optional back title.\n */\n title?: string;\n}\n\n/**\n * Native navbar state.\n */\nexport interface NativeNavigationNavbarOptions {\n /**\n * Hide the native navbar.\n */\n hidden?: boolean;\n\n /**\n * Main title.\n */\n title?: string;\n\n /**\n * Secondary title where supported by the platform.\n */\n subtitle?: string;\n\n /**\n * Prefer a large iOS title style.\n */\n large?: boolean;\n\n /**\n * Prefer transparent/scroll-edge style.\n */\n transparent?: boolean;\n\n /**\n * iOS blur/material effect for the navbar background when glass is not\n * available. Defaults to `systemChromeMaterial` for transparent bars.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Optional glass background behavior. Overrides `configure({ glass })` for\n * this navbar update.\n */\n glass?: NativeNavigationGlassOptions;\n\n /**\n * Back button state.\n */\n backButton?: NativeNavigationBackButton;\n\n /**\n * Left-side action buttons.\n */\n leftItems?: NativeNavigationBarButton[];\n\n /**\n * Right-side action buttons.\n */\n rightItems?: NativeNavigationBarButton[];\n\n /**\n * Navbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * Animate native navbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * A native tab item.\n */\nexport interface NativeNavigationTab {\n /**\n * Stable tab id returned in `tabSelect`.\n */\n id: string;\n\n /**\n * Visible tab label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Optional selected-state icon.\n */\n selectedIcon?: NativeNavigationIcon;\n\n /**\n * Optional badge. Numeric badges are supported on both platforms; text badge\n * support depends on platform capabilities.\n */\n badge?: string | number;\n\n /**\n * Whether the tab is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n\n /**\n * Hide the tab item from the native tabbar. When the hidden tab is selected,\n * native platform constraints may keep it visible until another tab is\n * selected.\n */\n hidden?: boolean;\n\n /**\n * Optional tab role. On floating tabbars, `search` (or `prominent` when\n * available) becomes a detached trailing circular action beside the capsule\n * — including the iOS 26+ system Liquid Glass tab bar, Android floating\n * layout, and the custom floating capsule path. Only one detached trailing\n * role is used; if multiple tabs set `search` or `prominent`, the last one\n * wins. Curve-shaped bars ignore `role` and keep using the included center\n * action. Defaults to `normal`.\n */\n role?: NativeNavigationTabRole;\n}\n\n/**\n * Native tabbar layout and background shape options.\n */\nexport interface NativeNavigationTabbarStyle {\n /**\n * `floating` keeps the capsule tabbar. On iOS 26+ this uses the\n * system-owned Liquid Glass `UITabBarController` unless a custom capsule\n * path is required; earlier iOS and the custom capsule path use\n * `UIGlassEffect` on iOS 26+ (blur material fallback otherwise). `curve`\n * draws a full-width bar with an included center action.\n */\n shape?: NativeNavigationTabbarShape;\n\n /**\n * Bar height in native points/dp, not including the system bottom inset.\n * Curve defaults to `49` on iOS (83pt once the home indicator is added) and\n * `80` on Android (Material navigation bar). Floating defaults to `64`.\n * Pass `height` to override either platform.\n */\n height?: number;\n\n /**\n * Horizontal margin in native points/dp. Defaults to `24` for `floating` and\n * `0` for `curve`.\n */\n horizontalMargin?: number;\n\n /**\n * Maximum tabbar width in native points/dp. Defaults to `430` for `floating`;\n * `curve` uses the available width unless this is set.\n */\n maxWidth?: number;\n\n /**\n * Bottom gap above the platform safe area in native points/dp. Defaults to\n * `10` for `floating` and `0` for `curve`.\n */\n bottomGap?: number;\n\n /**\n * Background corner radius in native points/dp. Defaults to a capsule radius\n * for `floating` and `0` for `curve`.\n */\n cornerRadius?: number;\n\n /**\n * Tab id promoted into the included center button for `curve`. Defaults to\n * the middle tab.\n */\n centerItemId?: string;\n\n /**\n * Included center button diameter in native points/dp. Defaults to `56`.\n */\n centerButtonDiameter?: number;\n\n /**\n * Distance from the top of the center button to the top edge of the bar in\n * native points/dp. Defaults to half of `centerButtonDiameter`.\n */\n centerButtonLift?: number;\n\n /**\n * Included center button color. Defaults to the active tint color.\n */\n centerButtonColor?: string;\n\n /**\n * Included center button icon color. Defaults to white.\n */\n centerButtonIconColor?: string;\n}\n\n/**\n * Native tabbar state.\n */\nexport interface NativeNavigationTabbarOptions {\n /**\n * Hide the native tabbar.\n */\n hidden?: boolean;\n\n /**\n * Tab definitions.\n */\n tabs?: NativeNavigationTab[];\n\n /**\n * Currently selected tab id.\n */\n selectedId?: string;\n\n /**\n * Show text labels. Defaults to `true`.\n */\n labels?: boolean;\n\n /**\n * Native label visibility mode. Overrides `labels` when provided.\n */\n labelVisibilityMode?: NativeNavigationTabLabelVisibilityMode;\n\n /**\n * Show icons. Defaults to `true`.\n */\n icons?: boolean;\n\n /**\n * Tabbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * iOS blur/material effect for the tabbar background when glass is not\n * available.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Optional glass background behavior. Overrides `configure({ glass })` for\n * this tabbar update.\n */\n glass?: NativeNavigationGlassOptions;\n\n /**\n * Opt into the iOS 26 Liquid Glass tint workaround that renders active and\n * inactive tab items into baked images. This can affect badge positioning\n * and icon sizing, so it is disabled by default.\n */\n experimentalBakedTintColors?: boolean;\n\n /**\n * Keep the iOS scroll-edge tabbar appearance from becoming transparent.\n * Mirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option.\n * Defaults to `false`.\n */\n disableTransparentOnScrollEdge?: boolean;\n\n /**\n * Disable the Android active tab indicator.\n */\n disableIndicator?: boolean;\n\n /**\n * Active tab indicator color on Android. `colors.indicator` is also\n * supported.\n */\n indicatorColor?: string;\n\n /**\n * Tab press ripple color on Android. `colors.ripple` is also supported.\n */\n rippleColor?: string;\n\n /**\n * Badge background color. `colors.badgeBackground` is also supported.\n */\n badgeBackgroundColor?: string;\n\n /**\n * Badge text color. `colors.badgeText` is also supported.\n */\n badgeTextColor?: string;\n\n /**\n * Optional native tabbar layout and shape customization.\n */\n style?: NativeNavigationTabbarStyle;\n\n /**\n * Animate native tabbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * Insets exposed to web content.\n */\nexport interface NativeNavigationInsets {\n top: number;\n right: number;\n bottom: number;\n left: number;\n navbarHeight: number;\n tabbarHeight: number;\n}\n\n/**\n * Returned by methods that may change safe content bounds.\n */\nexport interface NativeNavigationInsetsResult {\n insets: NativeNavigationInsets;\n}\n\n/**\n * Begin a native transition transaction before JS changes route content.\n */\nexport interface NativeNavigationBeginTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions. Use viewport coordinates such as\n * those returned by `Element.getBoundingClientRect()`.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Finish a native transition transaction after JS has changed route content.\n */\nexport interface NativeNavigationFinishTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions when no active source was recorded.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Native transition result.\n */\nexport interface NativeNavigationTransitionResult {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Plugin version payload.\n */\nexport interface PluginVersionResult {\n /**\n * Version identifier returned by the platform implementation.\n */\n version: string;\n}\n\nexport interface NativeNavigationBackEvent {\n source: 'navbar';\n}\n\nexport interface NativeNavigationBarItemTapEvent {\n id: string;\n title?: string;\n placement: 'left' | 'right';\n}\n\nexport interface NativeNavigationTabSelectEvent {\n id: string;\n index: number;\n title?: string;\n}\n\nexport interface NativeNavigationSafeAreaChangedEvent {\n insets: NativeNavigationInsets;\n}\n\nexport interface NativeNavigationTransitionEvent {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Framework-agnostic native navigation chrome API.\n */\nexport interface NativeNavigationPlugin {\n /**\n * Configure the native chrome host and content inset behavior.\n */\n configure(options?: NativeNavigationConfigureOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native navbar.\n */\n setNavbar(options: NativeNavigationNavbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native tabbar.\n */\n setTabbar(options: NativeNavigationTabbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Capture the current WebView and prepare a native transition.\n */\n beginTransition(options?: NativeNavigationBeginTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Animate from the captured WebView snapshot to the current live WebView.\n */\n finishTransition(options?: NativeNavigationFinishTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Returns the platform implementation version marker.\n */\n getPluginVersion(): Promise<PluginVersionResult>;\n\n addListener(\n eventName: 'navbarBack',\n listenerFunc: (event: NativeNavigationBackEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'navbarItemTap',\n listenerFunc: (event: NativeNavigationBarItemTapEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'tabSelect',\n listenerFunc: (event: NativeNavigationTabSelectEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'safeAreaChanged',\n listenerFunc: (event: NativeNavigationSafeAreaChangedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionStart',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionEnd',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * Platform rendering preference for the native bars.\n */\nexport type NativeNavigationPlatformStyle = 'auto' | 'ios' | 'android';\n\n/**\n * How the plugin exposes native bar sizes to web content.\n */\nexport type NativeNavigationContentInsetMode = 'css' | 'none';\n\n/**\n * Navigation animation direction.\n */\nexport type NativeNavigationTransitionDirection = 'forward' | 'back' | 'root' | 'tab' | 'zoom' | 'none';\n\n/**\n * Native material/blur effect preference.\n */\nexport type NativeNavigationBlurEffect =\n | 'none'\n | 'systemDefault'\n | 'extraLight'\n | 'light'\n | 'dark'\n | 'regular'\n | 'prominent'\n | 'systemUltraThinMaterial'\n | 'systemThinMaterial'\n | 'systemMaterial'\n | 'systemThickMaterial'\n | 'systemChromeMaterial'\n | 'systemUltraThinMaterialLight'\n | 'systemThinMaterialLight'\n | 'systemMaterialLight'\n | 'systemThickMaterialLight'\n | 'systemChromeMaterialLight'\n | 'systemUltraThinMaterialDark'\n | 'systemThinMaterialDark'\n | 'systemMaterialDark'\n | 'systemThickMaterialDark'\n | 'systemChromeMaterialDark';\n\n/**\n * Native glass background rendering preference.\n */\nexport type NativeNavigationGlassEffect = 'none' | 'liquidGlass';\n\n/**\n * Native glass background configuration.\n */\nexport interface NativeNavigationGlassOptions {\n /**\n * `liquidGlass` enables the Android 12+ live blurred WebView backdrop for\n * native bars. Android 11 and older keep a translucent surface fallback. iOS\n * uses the platform-owned Liquid Glass behavior.\n */\n effect?: NativeNavigationGlassEffect;\n\n /**\n * Android blur radius in native dp for `liquidGlass`. Defaults to `18`.\n */\n blurRadius?: number;\n\n /**\n * Alpha multiplier for the tint surface over the glass backdrop. Defaults to\n * `0.62`.\n */\n surfaceAlpha?: number;\n}\n\n/**\n * Native tab label visibility behavior.\n */\nexport type NativeNavigationTabLabelVisibilityMode = 'auto' | 'selected' | 'labeled' | 'unlabeled';\n\n/**\n * A rectangle in WebView viewport coordinates, expressed in native points/dp.\n */\nexport interface NativeNavigationRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Native tabbar background shape.\n */\nexport type NativeNavigationTabbarShape = 'floating' | 'curve';\n\n/**\n * Native tab role for Liquid Glass tab bars.\n *\n * `search` (and `prominent` when the OS supports it) renders as a detached\n * trailing circular action beside the floating tab capsule — the Apple News /\n * Photos pattern. Only one detached trailing role is used; if multiple tabs\n * set `search` or `prominent`, the last one wins. Curve-shaped bars ignore\n * `role` and keep using the included center action instead.\n */\nexport type NativeNavigationTabRole = 'normal' | 'search' | 'prominent';\n\n/**\n * A serializable icon descriptor. Framework nodes are intentionally not accepted\n * because icons are rendered by native UI.\n */\nexport interface NativeNavigationIcon {\n /**\n * Cross-platform asset path or URL fallback.\n */\n src?: string;\n\n /**\n * Cross-platform inline SVG markup. The native renderers support common icon\n * shapes such as path, line, polyline, polygon, circle, and rect. SVG icons\n * are rendered as template images by default so native tint colors still\n * apply.\n */\n svg?: string;\n\n /**\n * Preferred rendered icon width in native points/dp. Defaults to `24`.\n */\n width?: number;\n\n /**\n * Preferred rendered icon height in native points/dp. Defaults to `24`.\n */\n height?: number;\n\n /**\n * When `true`, native tint colors are applied to the rendered SVG/image.\n * Defaults to `true`.\n */\n template?: boolean;\n\n /**\n * iOS-specific SF Symbol, bundled image name, or inline SVG.\n */\n ios?: {\n /**\n * SF Symbol name, for example `house.fill`.\n */\n sfSymbol?: string;\n\n /**\n * Bundled image name from the app asset catalog.\n */\n image?: string;\n\n /**\n * iOS-specific inline SVG markup.\n */\n svg?: string;\n };\n\n /**\n * Android-specific drawable resource, asset name, or inline SVG.\n */\n android?: {\n /**\n * Drawable resource name without the `R.drawable.` prefix.\n */\n resource?: string;\n\n /**\n * Bundled image asset name.\n */\n image?: string;\n\n /**\n * Android-specific inline SVG markup.\n */\n svg?: string;\n };\n}\n\n/**\n * Native bar colors. Use CSS-style hex strings (`#RRGGBB` or `#AARRGGBB`).\n */\nexport interface NativeNavigationColors {\n /**\n * When `true`, Android 12+ derives unspecified bar colors from Material You\n * system palettes. Explicit color fields still win.\n */\n dynamic?: boolean;\n\n /**\n * Tint color for active buttons/items.\n */\n tint?: string;\n\n /**\n * Color for inactive tab items. Ignored on iOS 26+ unless\n * `experimentalBakedTintColors` is enabled.\n */\n inactiveTint?: string;\n\n /**\n * Optional background tint. Ignored on iOS 26+ so UIKit can preserve the\n * system Liquid Glass navigation appearance. On Android, alpha is honored.\n * When `glass.effect` is not `liquidGlass`, floating tabbars still apply\n * `glass.surfaceAlpha` (from `configure` / `setTabbar`) to the fill tint.\n */\n background?: string;\n\n /**\n * Title and label text color where the native platform supports it.\n */\n foreground?: string;\n\n /**\n * Badge background color for native tab badges.\n */\n badgeBackground?: string;\n\n /**\n * Badge text color for native tab badges.\n */\n badgeText?: string;\n\n /**\n * Active tab indicator color on Android.\n */\n indicator?: string;\n\n /**\n * Tab press ripple color on Android.\n */\n ripple?: string;\n}\n\n/**\n * Global plugin configuration.\n */\nexport interface NativeNavigationConfigureOptions {\n /**\n * Enables or disables the native chrome host.\n */\n enabled?: boolean;\n\n /**\n * Native style preference. `auto` uses the current platform.\n */\n platformStyle?: NativeNavigationPlatformStyle;\n\n /**\n * When `css`, the plugin writes CSS variables on `document.documentElement`.\n */\n contentInsetMode?: NativeNavigationContentInsetMode;\n\n /**\n * Default native transition duration in milliseconds.\n */\n animationDuration?: number;\n\n /**\n * Shared color hints for native bars.\n */\n colors?: NativeNavigationColors;\n\n /**\n * Shared glass background defaults for native bars.\n */\n glass?: NativeNavigationGlassOptions;\n}\n\n/**\n * A button shown in the native navbar.\n */\nexport interface NativeNavigationBarButton {\n /**\n * Stable id returned in `navbarItemTap`.\n */\n id: string;\n\n /**\n * Visible text label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Whether the action is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n/**\n * Native back button configuration.\n */\nexport interface NativeNavigationBackButton {\n /**\n * Show the native back affordance.\n */\n visible?: boolean;\n\n /**\n * Optional back title.\n */\n title?: string;\n}\n\n/**\n * Native navbar state.\n */\nexport interface NativeNavigationNavbarOptions {\n /**\n * Hide the native navbar.\n */\n hidden?: boolean;\n\n /**\n * Main title.\n */\n title?: string;\n\n /**\n * Secondary title where supported by the platform.\n */\n subtitle?: string;\n\n /**\n * Prefer a large iOS title style.\n */\n large?: boolean;\n\n /**\n * Prefer transparent/scroll-edge style.\n */\n transparent?: boolean;\n\n /**\n * iOS blur/material effect for the navbar background when glass is not\n * available. Defaults to `systemChromeMaterial` for transparent bars.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Optional glass background behavior. Overrides `configure({ glass })` for\n * this navbar update.\n */\n glass?: NativeNavigationGlassOptions;\n\n /**\n * Back button state.\n */\n backButton?: NativeNavigationBackButton;\n\n /**\n * Left-side action buttons.\n */\n leftItems?: NativeNavigationBarButton[];\n\n /**\n * Right-side action buttons.\n */\n rightItems?: NativeNavigationBarButton[];\n\n /**\n * Navbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * Animate native navbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * A native tab item.\n */\nexport interface NativeNavigationTab {\n /**\n * Stable tab id returned in `tabSelect`.\n */\n id: string;\n\n /**\n * Visible tab label.\n */\n title?: string;\n\n /**\n * Native icon descriptor.\n */\n icon?: NativeNavigationIcon;\n\n /**\n * Optional selected-state icon.\n */\n selectedIcon?: NativeNavigationIcon;\n\n /**\n * Optional badge. Numeric badges are supported on both platforms; text badge\n * support depends on platform capabilities.\n */\n badge?: string | number;\n\n /**\n * Whether the tab is enabled. Defaults to `true`.\n */\n enabled?: boolean;\n\n /**\n * Hide the tab item from the native tabbar. When the hidden tab is selected,\n * native platform constraints may keep it visible until another tab is\n * selected.\n */\n hidden?: boolean;\n\n /**\n * Optional tab role. On floating tabbars, `search` (or `prominent` when\n * available) becomes a detached trailing circular action beside the capsule\n * — including the iOS 26+ system Liquid Glass tab bar, Android floating\n * layout, and the custom floating capsule path. Only one detached trailing\n * role is used; if multiple tabs set `search` or `prominent`, the last one\n * wins. Curve-shaped bars ignore `role` and keep using the included center\n * action. Defaults to `normal`.\n */\n role?: NativeNavigationTabRole;\n}\n\n/**\n * Native tabbar layout and background shape options.\n */\nexport interface NativeNavigationTabbarStyle {\n /**\n * `floating` keeps the capsule tabbar. On iOS 26+ this uses the\n * system-owned Liquid Glass `UITabBarController` unless a custom capsule\n * path is required; earlier iOS and the custom capsule path use\n * `UIGlassEffect` on iOS 26+ (blur material fallback otherwise). `curve`\n * draws a full-width bar with an included center action.\n */\n shape?: NativeNavigationTabbarShape;\n\n /**\n * Bar height in native points/dp, not including the system bottom inset.\n * Curve defaults to `49` on iOS (83pt once the home indicator is added) and\n * `80` on Android (Material navigation bar). Floating defaults to `64`.\n * Pass `height` to override either platform.\n */\n height?: number;\n\n /**\n * Horizontal margin in native points/dp. Defaults to `24` for `floating` and\n * `0` for `curve`.\n */\n horizontalMargin?: number;\n\n /**\n * Maximum tabbar width in native points/dp. Defaults to `430` for `floating`;\n * `curve` uses the available width unless this is set.\n */\n maxWidth?: number;\n\n /**\n * Bottom gap above the platform safe area in native points/dp. Defaults to\n * `10` for `floating` and `0` for `curve`.\n */\n bottomGap?: number;\n\n /**\n * Background corner radius in native points/dp. Defaults to a capsule radius\n * for `floating` and `0` for `curve`.\n */\n cornerRadius?: number;\n\n /**\n * Tab id promoted into the included center button for `curve`. Defaults to\n * the middle tab.\n */\n centerItemId?: string;\n\n /**\n * Included center button diameter in native points/dp. Defaults to `56`.\n */\n centerButtonDiameter?: number;\n\n /**\n * Distance from the top of the center button to the top edge of the bar in\n * native points/dp. Defaults to half of `centerButtonDiameter`.\n */\n centerButtonLift?: number;\n\n /**\n * Included center button color. Defaults to the active tint color.\n */\n centerButtonColor?: string;\n\n /**\n * Included center button icon color. Defaults to white.\n */\n centerButtonIconColor?: string;\n}\n\n/**\n * Native tabbar state.\n */\nexport interface NativeNavigationTabbarOptions {\n /**\n * Hide the native tabbar.\n */\n hidden?: boolean;\n\n /**\n * Tab definitions.\n */\n tabs?: NativeNavigationTab[];\n\n /**\n * Currently selected tab id.\n */\n selectedId?: string;\n\n /**\n * Show text labels. Defaults to `true`.\n */\n labels?: boolean;\n\n /**\n * Native label visibility mode. Overrides `labels` when provided.\n */\n labelVisibilityMode?: NativeNavigationTabLabelVisibilityMode;\n\n /**\n * Show icons. Defaults to `true`.\n */\n icons?: boolean;\n\n /**\n * Tabbar color hints.\n */\n colors?: NativeNavigationColors;\n\n /**\n * iOS blur/material effect for the tabbar background when glass is not\n * available.\n */\n blurEffect?: NativeNavigationBlurEffect;\n\n /**\n * Optional glass background behavior. Overrides `configure({ glass })` for\n * this tabbar update. On Android 12+, floating tabbars default to\n * `liquidGlass` (live blur plus tint) when this field is omitted. Pass\n * `effect: 'none'` for a flat translucent fill without blur.\n */\n glass?: NativeNavigationGlassOptions;\n\n /**\n * Opt into the iOS 26 Liquid Glass tint workaround that renders active and\n * inactive tab items into baked images. This can affect badge positioning\n * and icon sizing, so it is disabled by default.\n */\n experimentalBakedTintColors?: boolean;\n\n /**\n * Keep the iOS scroll-edge tabbar appearance from becoming transparent.\n * Mirrors Expo Router native tabs' `disableTransparentOnScrollEdge` option.\n * Defaults to `false`.\n */\n disableTransparentOnScrollEdge?: boolean;\n\n /**\n * Disable the Android active tab indicator.\n */\n disableIndicator?: boolean;\n\n /**\n * Active tab indicator color on Android. `colors.indicator` is also\n * supported.\n */\n indicatorColor?: string;\n\n /**\n * Tab press ripple color on Android. `colors.ripple` is also supported.\n */\n rippleColor?: string;\n\n /**\n * Badge background color. `colors.badgeBackground` is also supported.\n */\n badgeBackgroundColor?: string;\n\n /**\n * Badge text color. `colors.badgeText` is also supported.\n */\n badgeTextColor?: string;\n\n /**\n * Optional native tabbar layout and shape customization.\n */\n style?: NativeNavigationTabbarStyle;\n\n /**\n * Animate native tabbar changes.\n */\n animated?: boolean;\n}\n\n/**\n * Insets exposed to web content.\n */\nexport interface NativeNavigationInsets {\n top: number;\n right: number;\n bottom: number;\n left: number;\n navbarHeight: number;\n tabbarHeight: number;\n}\n\n/**\n * Returned by methods that may change safe content bounds.\n */\nexport interface NativeNavigationInsetsResult {\n insets: NativeNavigationInsets;\n}\n\n/**\n * Begin a native transition transaction before JS changes route content.\n */\nexport interface NativeNavigationBeginTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions. Use viewport coordinates such as\n * those returned by `Element.getBoundingClientRect()`.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Finish a native transition transaction after JS has changed route content.\n */\nexport interface NativeNavigationFinishTransitionOptions {\n id?: string;\n direction?: NativeNavigationTransitionDirection;\n duration?: number;\n /**\n * Source rectangle for `zoom` transitions when no active source was recorded.\n */\n sourceRect?: NativeNavigationRect;\n /**\n * Destination rectangle for shared-element-style `zoom` transitions.\n */\n targetRect?: NativeNavigationRect;\n /**\n * Corner radius used while animating a `zoom` transition.\n */\n cornerRadius?: number;\n}\n\n/**\n * Native transition result.\n */\nexport interface NativeNavigationTransitionResult {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Plugin version payload.\n */\nexport interface PluginVersionResult {\n /**\n * Version identifier returned by the platform implementation.\n */\n version: string;\n}\n\nexport interface NativeNavigationBackEvent {\n source: 'navbar';\n}\n\nexport interface NativeNavigationBarItemTapEvent {\n id: string;\n title?: string;\n placement: 'left' | 'right';\n}\n\nexport interface NativeNavigationTabSelectEvent {\n id: string;\n index: number;\n title?: string;\n}\n\nexport interface NativeNavigationSafeAreaChangedEvent {\n insets: NativeNavigationInsets;\n}\n\nexport interface NativeNavigationTransitionEvent {\n id: string;\n direction: NativeNavigationTransitionDirection;\n duration: number;\n}\n\n/**\n * Framework-agnostic native navigation chrome API.\n */\nexport interface NativeNavigationPlugin {\n /**\n * Configure the native chrome host and content inset behavior.\n */\n configure(options?: NativeNavigationConfigureOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native navbar.\n */\n setNavbar(options: NativeNavigationNavbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Render or update the native tabbar.\n */\n setTabbar(options: NativeNavigationTabbarOptions): Promise<NativeNavigationInsetsResult>;\n\n /**\n * Capture the current WebView and prepare a native transition.\n */\n beginTransition(options?: NativeNavigationBeginTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Animate from the captured WebView snapshot to the current live WebView.\n */\n finishTransition(options?: NativeNavigationFinishTransitionOptions): Promise<NativeNavigationTransitionResult>;\n\n /**\n * Returns the platform implementation version marker.\n */\n getPluginVersion(): Promise<PluginVersionResult>;\n\n addListener(\n eventName: 'navbarBack',\n listenerFunc: (event: NativeNavigationBackEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'navbarItemTap',\n listenerFunc: (event: NativeNavigationBarItemTapEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'tabSelect',\n listenerFunc: (event: NativeNavigationTabSelectEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'safeAreaChanged',\n listenerFunc: (event: NativeNavigationSafeAreaChangedEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionStart',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: 'transitionEnd',\n listenerFunc: (event: NativeNavigationTransitionEvent) => void,\n ): Promise<PluginListenerHandle>;\n}\n"]}
@@ -2,7 +2,6 @@
2
2
 
3
3
  import Foundation
4
4
  import Capacitor
5
- import ObjectiveC
6
5
  import UIKit
7
6
 
8
7
  private struct NativeNavigationTransitionContext {
@@ -58,6 +57,9 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
58
57
  private var tabbarHeight: CGFloat = NativeNavigationTabbarStyleConfig().totalHeight
59
58
  private var navbarVisible = false
60
59
  private var tabbarVisible = false
60
+ private var tabbarSoftHidden = false
61
+ private var tabbarSkipImmediateSystemReveal = false
62
+ private var floatingTabBarWantsHidden = false
61
63
  private var contentInsetMode = "css"
62
64
  private var isEnabled = true
63
65
  private var defaultTransitionDuration: TimeInterval = 0.35
@@ -203,15 +205,36 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
203
205
  }
204
206
 
205
207
  let hidden = call.getBool("hidden", false)
208
+ let animated = call.getBool("animated", false)
206
209
  self.tabbarVisible = !hidden
207
210
 
208
211
  guard !hidden else {
209
- self.hideTabBarChrome()
212
+ self.hideTabBarChrome(animated: animated)
210
213
  self.updateInsetsAndNotify()
211
214
  call.resolve(self.insetsResult())
212
215
  return
213
216
  }
214
217
 
218
+ if self.tabbarSoftHidden {
219
+ self.tabbarSoftHidden = false
220
+ if animated {
221
+ if nativeNavigationPrefersAnimatedSystemTabbarVisibility(
222
+ animated: true,
223
+ usesSystemLiquidGlass: self.usesSystemLiquidGlass,
224
+ shape: self.tabbarStyle.shape == .curve ? "curve" : "floating"
225
+ ) {
226
+ self.tabbarSkipImmediateSystemReveal = true
227
+ self.setSystemTabBarHidden(false, animated: true, syncSubviewState: false)
228
+ } else {
229
+ self.setFloatingTabBarVisibility(hidden: false, animated: true)
230
+ }
231
+ } else if self.usesSystemLiquidGlass && self.tabbarStyle.shape != .curve {
232
+ self.setSystemTabBarHidden(false, animated: false, syncSubviewState: true)
233
+ } else {
234
+ self.setFloatingTabBarVisibility(hidden: false, animated: false)
235
+ }
236
+ }
237
+
215
238
  self.tabbarStyle = self.makeTabbarStyle(from: call)
216
239
  self.tabbarHeight = self.tabbarStyle.totalHeight
217
240
 
@@ -241,6 +264,7 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
241
264
  self.applyTabBarAppearance(tabBar: tabBar, options: call)
242
265
  if items.isEmpty {
243
266
  self.tabbarVisible = false
267
+ self.tabbarSkipImmediateSystemReveal = false
244
268
  self.hideSystemTabBarChromeCompletely()
245
269
  } else {
246
270
  self.showTabBarChrome(tabBar)
@@ -904,18 +928,22 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
904
928
  }
905
929
  }
906
930
 
907
- private func setSystemTabBarHidden(_ hidden: Bool) {
931
+ private func setSystemTabBarHidden(_ hidden: Bool, animated: Bool = false, syncSubviewState: Bool = true) {
908
932
  guard let tabBarController = tabBarController else {
909
933
  return
910
934
  }
911
935
 
912
936
  let tabBar = tabBarController.tabBar
913
937
  if #available(iOS 18.0, *) {
914
- tabBarController.setTabBarHidden(hidden, animated: false)
938
+ tabBarController.setTabBarHidden(hidden, animated: animated)
915
939
  } else {
916
940
  tabBar.isHidden = hidden
917
941
  }
918
942
 
943
+ guard syncSubviewState else {
944
+ return
945
+ }
946
+
919
947
  if hidden {
920
948
  setTabBarBackgroundSubviewsHidden(true, on: tabBar)
921
949
  } else {
@@ -943,7 +971,24 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
943
971
  floatingTabBar?.isHidden = true
944
972
  }
945
973
 
946
- private func hideTabBarChrome() {
974
+ private func hideTabBarChrome(animated: Bool = false) {
975
+ if nativeNavigationPrefersAnimatedSystemTabbarVisibility(
976
+ animated: animated,
977
+ usesSystemLiquidGlass: usesSystemLiquidGlass,
978
+ shape: tabbarStyle.shape == .curve ? "curve" : "floating"
979
+ ) {
980
+ tabbarSoftHidden = true
981
+ setSystemTabBarHidden(true, animated: true, syncSubviewState: false)
982
+ return
983
+ }
984
+
985
+ if animated {
986
+ tabbarSoftHidden = true
987
+ setFloatingTabBarVisibility(hidden: true, animated: true)
988
+ return
989
+ }
990
+
991
+ tabbarSoftHidden = false
947
992
  if usesSystemLiquidGlass && tabbarStyle.shape != .curve {
948
993
  hideSystemTabBarChromeCompletely()
949
994
  } else {
@@ -953,12 +998,51 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
953
998
  }
954
999
  }
955
1000
 
1001
+ private func setFloatingTabBarVisibility(hidden: Bool, animated: Bool) {
1002
+ floatingTabBarWantsHidden = hidden
1003
+ let targets = [tabContainer, floatingTabBar].compactMap { $0 }
1004
+ guard !targets.isEmpty else {
1005
+ return
1006
+ }
1007
+
1008
+ if !animated {
1009
+ targets.forEach { view in
1010
+ view.isHidden = hidden
1011
+ view.alpha = hidden ? 0 : 1
1012
+ }
1013
+ return
1014
+ }
1015
+
1016
+ if hidden {
1017
+ UIView.animate(withDuration: 0.2, animations: {
1018
+ targets.forEach { $0.alpha = 0 }
1019
+ }, completion: { finished in
1020
+ guard finished, self.floatingTabBarWantsHidden else {
1021
+ return
1022
+ }
1023
+ targets.forEach { $0.isHidden = true }
1024
+ })
1025
+ return
1026
+ }
1027
+
1028
+ targets.forEach { view in
1029
+ view.isHidden = false
1030
+ view.alpha = 0
1031
+ }
1032
+ UIView.animate(withDuration: 0.2) {
1033
+ targets.forEach { $0.alpha = 1 }
1034
+ }
1035
+ }
1036
+
956
1037
  private func showTabBarChrome(_ tabBar: UITabBar) {
957
1038
  tabContainer?.isHidden = true
958
1039
  floatingTabBar?.isHidden = true
959
1040
  tabBarController?.view.isHidden = false
960
1041
  if usesSystemLiquidGlass && tabbarStyle.shape != .curve {
961
- setSystemTabBarHidden(false)
1042
+ if !tabbarSkipImmediateSystemReveal {
1043
+ setSystemTabBarHidden(false)
1044
+ }
1045
+ tabbarSkipImmediateSystemReveal = false
962
1046
  liftWebViewOverlaysAboveSystemTabs()
963
1047
  hostWebViewInSelectedSystemTab()
964
1048
  } else {
@@ -1967,44 +2051,25 @@ public class NativeNavigationPlugin: CAPPlugin, CAPBridgedPlugin, UITabBarContro
1967
2051
  }
1968
2052
 
1969
2053
  private func liquidGlassEffect() -> UIVisualEffect? {
1970
- guard usesSystemLiquidGlass,
1971
- let effectClass = NSClassFromString("UIGlassEffect") else {
2054
+ guard usesSystemLiquidGlass else {
1972
2055
  return nil
1973
2056
  }
1974
-
1975
- let styleSelector = NSSelectorFromString("effectWithStyle:")
1976
- if let method = class_getClassMethod(effectClass, styleSelector) {
1977
- typealias EffectWithStyle = @convention(c) (AnyClass, Selector, Int) -> AnyObject?
1978
- let factory = unsafeBitCast(method_getImplementation(method), to: EffectWithStyle.self)
1979
- if let effect = factory(effectClass, styleSelector, 0) as? UIVisualEffect {
1980
- return effect
1981
- }
1982
- }
1983
-
1984
- if let objectType = effectClass as? NSObject.Type {
1985
- return objectType.init() as? UIVisualEffect
2057
+ #if compiler(>=6.2)
2058
+ if #available(iOS 26.0, *) {
2059
+ return UIGlassEffect(style: .regular)
1986
2060
  }
1987
-
2061
+ #endif
1988
2062
  return nil
1989
2063
  }
1990
2064
 
1991
2065
  private func configureGlassBarButtonItem(_ item: UIBarButtonItem, id: String) {
1992
- guard #available(iOS 26.0, *) else {
1993
- return
1994
- }
1995
-
1996
- // Keep older SDK builds working while adopting the native iOS 26 bar
1997
- // button Liquid Glass grouping APIs when the runtime exposes them.
1998
- let object = item as NSObject
1999
- if object.responds(to: NSSelectorFromString("setIdentifier:")) {
2000
- object.setValue(id, forKey: "identifier")
2001
- }
2002
- if object.responds(to: NSSelectorFromString("setSharesBackground:")) {
2003
- object.setValue(true, forKey: "sharesBackground")
2004
- }
2005
- if object.responds(to: NSSelectorFromString("setHidesSharedBackground:")) {
2006
- object.setValue(false, forKey: "hidesSharedBackground")
2066
+ #if compiler(>=6.2)
2067
+ if #available(iOS 26.0, *) {
2068
+ item.identifier = id
2069
+ item.sharesBackground = true
2070
+ item.hidesSharedBackground = false
2007
2071
  }
2072
+ #endif
2008
2073
  }
2009
2074
 
2010
2075
  private func currentInsets() -> [String: Any] {
@@ -2780,6 +2845,14 @@ func nativeNavigationUsesStationaryTransitionCrossfade(direction: String) -> Boo
2780
2845
  direction == "tab" || direction == "root" || direction == "none"
2781
2846
  }
2782
2847
 
2848
+ func nativeNavigationPrefersAnimatedSystemTabbarVisibility(
2849
+ animated: Bool,
2850
+ usesSystemLiquidGlass: Bool,
2851
+ shape: String
2852
+ ) -> Bool {
2853
+ animated && usesSystemLiquidGlass && shape != "curve"
2854
+ }
2855
+
2783
2856
  private func nativeNavigationNeedsTransitionSurface(_ color: UIColor?) -> Bool {
2784
2857
  guard let color else {
2785
2858
  return true
@@ -97,4 +97,28 @@ class NativeNavigationTests: XCTestCase {
97
97
  XCTAssertFalse(nativeNavigationUsesStationaryTransitionCrossfade(direction: "forward"))
98
98
  XCTAssertFalse(nativeNavigationUsesStationaryTransitionCrossfade(direction: "back"))
99
99
  }
100
+
101
+ func testAnimatedSystemTabbarVisibilityUsesSoftHide() {
102
+ XCTAssertTrue(
103
+ nativeNavigationPrefersAnimatedSystemTabbarVisibility(
104
+ animated: true,
105
+ usesSystemLiquidGlass: true,
106
+ shape: "floating"
107
+ )
108
+ )
109
+ XCTAssertFalse(
110
+ nativeNavigationPrefersAnimatedSystemTabbarVisibility(
111
+ animated: false,
112
+ usesSystemLiquidGlass: true,
113
+ shape: "floating"
114
+ )
115
+ )
116
+ XCTAssertFalse(
117
+ nativeNavigationPrefersAnimatedSystemTabbarVisibility(
118
+ animated: true,
119
+ usesSystemLiquidGlass: true,
120
+ shape: "curve"
121
+ )
122
+ )
123
+ }
100
124
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-native-navigation",
3
- "version": "8.4.5",
3
+ "version": "8.6.0",
4
4
  "description": "Capacitor plugin for native navbar, tabbar, safe-area handling, and WebView snapshot transitions.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",