mac-human-design 0.1.5 → 0.1.6

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,6 +3,7 @@
3
3
  A reusable shared library for Tauri macOS apps. It includes:
4
4
 
5
5
  - macOS-styled wrappers for every public `@base-ui/react` component family.
6
+ - Default-on Motion-powered animations that respect reduced-motion settings.
6
7
  - `MacSegmentedControl` and macOS design tokens/classes.
7
8
  - A reusable SF Symbols fetch layer with React hooks/components.
8
9
  - A Rust/Tauri plugin crate that exposes a native command:
@@ -19,11 +20,12 @@ A reusable shared library for Tauri macOS apps. It includes:
19
20
  From the consumer app:
20
21
 
21
22
  ```bash
22
- npm i mac-human-design @base-ui/react react react-dom
23
+ npm i mac-human-design @base-ui/react motion react react-dom
23
24
  ```
24
25
 
25
- `@base-ui/react`, `react`, and `react-dom` are peer dependencies. Install
26
- `@tauri-apps/api` too when using the SF Symbols helpers or Tauri integration.
26
+ `@base-ui/react`, `motion`, `react`, and `react-dom` are peer dependencies.
27
+ Install `@tauri-apps/api` too when using the SF Symbols helpers or Tauri
28
+ integration.
27
29
 
28
30
  Consumer apps should also include the macOS Base UI stylesheet once near their
29
31
  app root:
@@ -54,7 +56,7 @@ fn main() {
54
56
 
55
57
  ```ts
56
58
  import { useState } from "react";
57
- import { MacButton, MacSegmentedControl, MacSelect } from "mac-human-design";
59
+ import { MacButton, MacMotionProvider, MacSegmentedControl, MacSelect } from "mac-human-design";
58
60
  import { SystemSymbolImage } from "mac-human-design/symbols";
59
61
  import "mac-human-design/styles/macos-base-ui.css";
60
62
 
@@ -62,7 +64,7 @@ export function Demo() {
62
64
  const [tab, setTab] = useState<"files" | "settings">("files");
63
65
 
64
66
  return (
65
- <div>
67
+ <MacMotionProvider>
66
68
  <SystemSymbolImage symbolName="folder" sizePx={18} fallback="▢" />
67
69
  <MacSegmentedControl
68
70
  options={[
@@ -78,7 +80,7 @@ export function Demo() {
78
80
  <MacSelect.Value placeholder="Choose" />
79
81
  </MacSelect.Trigger>
80
82
  </MacSelect.Root>
81
- </div>
83
+ </MacMotionProvider>
82
84
  );
83
85
  }
84
86
  ```
@@ -109,7 +111,7 @@ The following components are now in the shared library and can be imported by an
109
111
  `MacHelpButton`, `MacCheckbox`, `MacCheckboxGroup`, `MacCollapsible`,
110
112
  `MacCombobox`, `MacContextMenu`, `MacDialog`, `MacDrawer`, `MacField`,
111
113
  `MacFieldset`, `MacForm`, `MacInput`, `MacTextField`, `MacSearchField`,
112
- `MacMenu`, `MacMenubar`, `MacMeter`, `MacNavigationMenu`,
114
+ `MacMenu`, `MacMenubar`, `MacMeter`, `MacMotionProvider`, `MacNavigationMenu`,
113
115
  `MacNumberField`, `MacOTPField`, `MacPopover`,
114
116
  `MacPreviewCard`, `MacProgress`, `MacRadio`, `MacRadioGroup`,
115
117
  `MacScrollArea`, `MacSelect`, `MacSeparator`, `MacSlider`, `MacSwitch`,
package/changelog.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  This file tracks changes between published npm versions of `mac-human-design`.
4
4
 
5
+ ## 0.1.6
6
+
7
+ ### Added
8
+
9
+ - Added Motion for React as the default animation layer for the macOS Base UI
10
+ wrappers.
11
+ - Added `MacMotionProvider`, which applies the shared Motion transition config
12
+ and respects the user's reduced-motion preference by default.
13
+ - Added default macOS motion presets for controls, fields, selection controls,
14
+ panels, popups, dialogs, drawers, indicators, feedback meters, and utility
15
+ surfaces.
16
+ - Added verifier coverage for Motion peer/dev dependencies, provider export,
17
+ reduced-motion config, default transitions, and safe module-scope
18
+ `motion.create()` usage.
19
+
20
+ ### Changed
21
+
22
+ - Bumped the package to `0.1.6`.
23
+ - Added `motion` as a required peer dependency and development dependency.
24
+ - Updated the gallery to render inside `MacMotionProvider` so animation defaults
25
+ are exercised by the shipped example app.
26
+
5
27
  ## 0.1.5
6
28
 
7
29
  ### Changed
@@ -7,8 +7,10 @@
7
7
  import "mac-human-design/styles/macos-base-ui.css";
8
8
  ```
9
9
 
10
- Consumer apps install `@base-ui/react`, `react`, and `react-dom` as peer
11
- dependencies alongside this package.
10
+ Consumer apps install `@base-ui/react`, `motion`, `react`, and `react-dom` as
11
+ peer dependencies alongside this package. Motion animations are enabled by
12
+ default through the macOS wrappers and respect the user's reduced-motion
13
+ preference through `MacMotionProvider`.
12
14
 
13
15
  ## Component Families
14
16
 
@@ -70,6 +72,7 @@ render UI.
70
72
  | Borderless button | `MacPlainButton` |
71
73
  | Icon button | `MacIconButton` |
72
74
  | Help button | `MacHelpButton` |
75
+ | Motion defaults provider | `MacMotionProvider` |
73
76
  | Text field | `MacTextField` / `MacInput` |
74
77
  | Search field | `MacSearchField` |
75
78
  | Pop-up button | `MacSelect.PopUpButtonTrigger` |
@@ -104,6 +107,8 @@ The gate typechecks the library and verifies:
104
107
  - macOS design features are present: system font stack, dark mode, reduced motion,
105
108
  keyboard focus rings, accent tokens, styled `hd-mac` classes, and typography
106
109
  constraints,
110
+ - Motion integration is present: dependency declarations, `MacMotionProvider`,
111
+ reduced-motion config, default transitions, and module-scope `motion.create()`,
107
112
  - the visual gallery can be bundled for production from the shipped source,
108
113
  - old UI-foundation references have not returned.
109
114
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mac-human-design",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Reusable macOS-oriented UI primitives and SF Symbols bridge for Tauri apps.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -49,6 +49,7 @@
49
49
  "peerDependencies": {
50
50
  "@base-ui/react": "^1.5.0",
51
51
  "@tauri-apps/api": "^2",
52
+ "motion": "^12.40.0",
52
53
  "react": ">=18",
53
54
  "react-dom": ">=18"
54
55
  },
@@ -63,6 +64,7 @@
63
64
  "@types/react": "^19.2.17",
64
65
  "@types/react-dom": "^19.2.3",
65
66
  "@vitejs/plugin-react": "^6.0.2",
67
+ "motion": "^12.40.0",
66
68
  "react": "^19.2.7",
67
69
  "react-dom": "^19.2.7",
68
70
  "typescript": "^6.0.3",
@@ -47,7 +47,10 @@ function getBaseUiFamilies() {
47
47
 
48
48
  function getMacExports(source) {
49
49
  return new Set(
50
- Array.from(source.matchAll(/^export const (Mac[A-Za-z0-9]+)\b/gm), (match) => match[1]),
50
+ Array.from(
51
+ source.matchAll(/^export (?:const|function) (Mac[A-Za-z0-9]+)\b/gm),
52
+ (match) => match[1],
53
+ ),
51
54
  );
52
55
  }
53
56
 
@@ -157,6 +160,7 @@ if (missingDocsRows.length > 0) {
157
160
  }
158
161
 
159
162
  const requiredVariants = [
163
+ "MacMotionProvider",
160
164
  "MacPrimaryButton",
161
165
  "MacSecondaryButton",
162
166
  "MacDestructiveButton",
@@ -269,5 +273,5 @@ if (process.exitCode) {
269
273
  }
270
274
 
271
275
  console.log(
272
- `Verified ${families.length} @base-ui/react component families, namespace parts, ${requiredVariants.length} native variants, ${requiredNestedVariants.length} nested variants, ${wrapperClasses.size} wrapper classes, gallery coverage, and no Base Web references.`,
276
+ `Verified ${families.length} @base-ui/react component families, namespace parts, ${requiredVariants.length} provider/variant exports, ${requiredNestedVariants.length} nested variants, ${wrapperClasses.size} wrapper classes, gallery coverage, and no Base Web references.`,
273
277
  );
@@ -4,6 +4,8 @@ import { fileURLToPath } from "node:url";
4
4
 
5
5
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
6
6
  const stylesPath = path.join(repoRoot, "src", "styles", "macosBaseUi.css");
7
+ const packageJsonPath = path.join(repoRoot, "package.json");
8
+ const wrapperPath = path.join(repoRoot, "src", "components", "MacBaseUI.tsx");
7
9
 
8
10
  function readText(filePath) {
9
11
  return fs.readFileSync(filePath, "utf8");
@@ -39,6 +41,8 @@ const sources = new Map(
39
41
  sourceFiles.map((relativeFile) => [relativeFile, readText(path.join(repoRoot, relativeFile))]),
40
42
  );
41
43
  const stylesSource = readText(stylesPath);
44
+ const packageJson = JSON.parse(readText(packageJsonPath));
45
+ const wrapperSource = readText(wrapperPath);
42
46
 
43
47
  const requiredStyleFeatures = [
44
48
  ["system font stack", /-apple-system,\s*BlinkMacSystemFont/],
@@ -56,6 +60,44 @@ if (missingFeatures.length > 0) {
56
60
  fail("Missing required macOS styling features.", missingFeatures);
57
61
  }
58
62
 
63
+ const missingMotionDependencies = [];
64
+ if (!packageJson.peerDependencies?.motion) {
65
+ missingMotionDependencies.push("peerDependencies.motion");
66
+ }
67
+ if (!packageJson.devDependencies?.motion) {
68
+ missingMotionDependencies.push("devDependencies.motion");
69
+ }
70
+
71
+ if (missingMotionDependencies.length > 0) {
72
+ fail("Missing required Motion dependency declarations.", missingMotionDependencies);
73
+ }
74
+
75
+ const requiredMotionFeatures = [
76
+ ["MotionConfig import", /import\s+\{[\s\S]*MotionConfig[\s\S]*\}\s+from\s+["']motion\/react["']/],
77
+ ["motion import", /import\s+\{[\s\S]*\bmotion\b[\s\S]*\}\s+from\s+["']motion\/react["']/],
78
+ ["MacMotionProvider export", /export function MacMotionProvider\b/],
79
+ ["MotionProps wrapper typing", /React\.ComponentPropsWithoutRef<C> & MotionProps/],
80
+ ["reduced motion user config", /reducedMotion\s*=\s*["']user["']/],
81
+ ["motion.create wrapper", /motion\.create\(BaseWrapped as AnyComponent\)/],
82
+ ["default Motion transition", /const macDefaultTransition:\s*Transition/],
83
+ ];
84
+
85
+ const missingMotionFeatures = requiredMotionFeatures
86
+ .filter(([, pattern]) => !pattern.test(wrapperSource))
87
+ .map(([label]) => label);
88
+
89
+ if (missingMotionFeatures.length > 0) {
90
+ fail("Missing required Motion integration features.", missingMotionFeatures);
91
+ }
92
+
93
+ const motionCreateMatches = Array.from(wrapperSource.matchAll(/motion\.create\(/g));
94
+ const allowedMotionCreatePattern = /^\s*const MotionPart = motion\.create\(BaseWrapped as AnyComponent\);$/m;
95
+ if (motionCreateMatches.length !== 1 || !allowedMotionCreatePattern.test(wrapperSource)) {
96
+ fail("motion.create() must be called exactly once in the module-level macPart wrapper setup.", [
97
+ "src/components/MacBaseUI.tsx",
98
+ ]);
99
+ }
100
+
59
101
  const negativeLetterSpacing = [];
60
102
  const viewportScaledFonts = [];
61
103
 
@@ -1,4 +1,12 @@
1
1
  import * as React from "react";
2
+ import {
3
+ MotionConfig,
4
+ motion,
5
+ type MotionConfigProps,
6
+ type MotionProps,
7
+ type TargetAndTransition,
8
+ type Transition,
9
+ } from "motion/react";
2
10
  import { Accordion as BaseAccordion } from "@base-ui/react/accordion";
3
11
  import { AlertDialog as BaseAlertDialog } from "@base-ui/react/alert-dialog";
4
12
  import { Autocomplete as BaseAutocomplete } from "@base-ui/react/autocomplete";
@@ -42,6 +50,47 @@ import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
42
50
 
43
51
  type AnyComponent = React.ComponentType<any>;
44
52
  type ClassNameProp = string | ((state: any) => string | undefined) | undefined;
53
+ type MacAnimatedComponent<C extends AnyComponent> = React.ForwardRefExoticComponent<
54
+ React.PropsWithoutRef<React.ComponentPropsWithoutRef<C> & MotionProps> & React.RefAttributes<unknown>
55
+ >;
56
+ type MotionDefaults = Pick<
57
+ MotionProps,
58
+ "initial" | "animate" | "exit" | "layout" | "whileFocus" | "whileHover" | "whileTap"
59
+ > & {
60
+ transition?: Transition;
61
+ };
62
+
63
+ const macDefaultTransition: Transition = {
64
+ default: { type: "spring", stiffness: 520, damping: 38, mass: 0.8 },
65
+ opacity: { duration: 0.12, ease: "easeOut" },
66
+ };
67
+
68
+ const macFastTransition: Transition = {
69
+ default: { type: "spring", stiffness: 680, damping: 44, mass: 0.7 },
70
+ opacity: { duration: 0.09, ease: "easeOut" },
71
+ };
72
+
73
+ const macSoftTransition: Transition = {
74
+ default: { type: "spring", stiffness: 420, damping: 34, mass: 0.9 },
75
+ opacity: { duration: 0.14, ease: "easeOut" },
76
+ };
77
+
78
+ export type MacMotionProviderProps = React.PropsWithChildren<{
79
+ reducedMotion?: MotionConfigProps["reducedMotion"];
80
+ transition?: MotionConfigProps["transition"];
81
+ }>;
82
+
83
+ export function MacMotionProvider({
84
+ children,
85
+ reducedMotion = "user",
86
+ transition = macDefaultTransition,
87
+ }: MacMotionProviderProps) {
88
+ return (
89
+ <MotionConfig reducedMotion={reducedMotion} transition={transition}>
90
+ {children}
91
+ </MotionConfig>
92
+ );
93
+ }
45
94
 
46
95
  function joinClasses(...classes: Array<string | undefined | false>) {
47
96
  return classes.filter(Boolean).join(" ");
@@ -54,8 +103,153 @@ function mergeClassName(macClassName: string, className: ClassNameProp): ClassNa
54
103
  return joinClasses(macClassName, className);
55
104
  }
56
105
 
106
+ function includesAny(value: string, tokens: string[]) {
107
+ return tokens.some((token) => value.includes(token));
108
+ }
109
+
110
+ function isDisabled(props: Record<string, unknown>) {
111
+ return Boolean(props.disabled || props["data-disabled"] || props["aria-disabled"]);
112
+ }
113
+
114
+ function interactiveControlDefaults(macClassName: string, props: Record<string, unknown>): MotionDefaults {
115
+ if (isDisabled(props)) {
116
+ return {};
117
+ }
118
+
119
+ return {
120
+ whileHover: { y: -0.5, scale: 1.012 },
121
+ whileTap: { y: 1, scale: 0.985 },
122
+ transition: macFastTransition,
123
+ };
124
+ }
125
+
126
+ function fadeScaleDefaults(
127
+ initial: TargetAndTransition,
128
+ animate: TargetAndTransition = { opacity: 1, scale: 1, y: 0, x: 0 },
129
+ transition: Transition = macSoftTransition,
130
+ ): MotionDefaults {
131
+ return {
132
+ initial,
133
+ animate,
134
+ exit: initial,
135
+ transition,
136
+ };
137
+ }
138
+
139
+ function getMotionDefaults(macClassName: string, props: Record<string, unknown>): MotionDefaults {
140
+ if (
141
+ includesAny(macClassName, [
142
+ "hd-mac-button",
143
+ "hd-mac-select-trigger",
144
+ "hd-mac-toggle",
145
+ "hd-mac-toolbar-button",
146
+ "hd-mac-stepper-button",
147
+ "hd-mac-disclosure-trigger",
148
+ ])
149
+ ) {
150
+ return interactiveControlDefaults(macClassName, props);
151
+ }
152
+
153
+ if (macClassName.includes("hd-mac-input")) {
154
+ return {
155
+ whileFocus: { scale: 1.004 },
156
+ transition: macFastTransition,
157
+ };
158
+ }
159
+
160
+ if (includesAny(macClassName, ["hd-mac-checkbox-indicator", "hd-mac-radio-indicator"])) {
161
+ return fadeScaleDefaults({ opacity: 0, scale: 0.72 }, { opacity: 1, scale: 1 });
162
+ }
163
+
164
+ if (macClassName.includes("hd-mac-switch-thumb") || macClassName.includes("hd-mac-slider-thumb")) {
165
+ return {
166
+ layout: true,
167
+ whileTap: { scale: 0.92 },
168
+ transition: macDefaultTransition,
169
+ };
170
+ }
171
+
172
+ if (includesAny(macClassName, ["hd-mac-progress-indicator", "hd-mac-meter-indicator", "hd-mac-slider-indicator"])) {
173
+ return {
174
+ layout: true,
175
+ transition: macSoftTransition,
176
+ };
177
+ }
178
+
179
+ if (includesAny(macClassName, ["hd-mac-popup", "hd-mac-popover", "hd-mac-preview-card"])) {
180
+ return fadeScaleDefaults({ opacity: 0, scale: 0.97, y: 4 });
181
+ }
182
+
183
+ if (macClassName.includes("hd-mac-tooltip")) {
184
+ return fadeScaleDefaults({ opacity: 0, scale: 0.96 }, { opacity: 1, scale: 1 }, macFastTransition);
185
+ }
186
+
187
+ if (macClassName.includes("hd-mac-backdrop")) {
188
+ return fadeScaleDefaults({ opacity: 0 }, { opacity: 1 }, { duration: 0.16, ease: "easeOut" });
189
+ }
190
+
191
+ if (macClassName.includes("hd-mac-sheet-dialog")) {
192
+ return fadeScaleDefaults({ opacity: 0, y: -18, scale: 0.985 });
193
+ }
194
+
195
+ if (macClassName.includes("hd-mac-alert-dialog")) {
196
+ return fadeScaleDefaults({ opacity: 0, scale: 0.94, y: 8 }, { opacity: 1, scale: 1, y: 0 });
197
+ }
198
+
199
+ if (macClassName.includes("hd-mac-dialog")) {
200
+ return fadeScaleDefaults({ opacity: 0, scale: 0.965, y: 10 });
201
+ }
202
+
203
+ if (macClassName.includes("hd-mac-sidebar-drawer")) {
204
+ return fadeScaleDefaults({ opacity: 0, x: -22 }, { opacity: 1, x: 0 });
205
+ }
206
+
207
+ if (macClassName.includes("hd-mac-drawer")) {
208
+ return fadeScaleDefaults({ opacity: 0, y: 18 }, { opacity: 1, y: 0 });
209
+ }
210
+
211
+ if (includesAny(macClassName, ["hd-mac-accordion-panel", "hd-mac-collapsible-panel", "hd-mac-tab-panel"])) {
212
+ return {
213
+ initial: { opacity: 0, y: 4 },
214
+ animate: { opacity: 1, y: 0 },
215
+ exit: { opacity: 0, y: 4 },
216
+ layout: true,
217
+ transition: macSoftTransition,
218
+ };
219
+ }
220
+
221
+ if (includesAny(macClassName, ["hd-mac-menu-item", "hd-mac-menu-row"])) {
222
+ return fadeScaleDefaults({ opacity: 0, x: -3 }, { opacity: 1, x: 0 }, macFastTransition);
223
+ }
224
+
225
+ if (includesAny(macClassName, ["hd-mac-menu-item-indicator", "hd-mac-control-icon"])) {
226
+ return fadeScaleDefaults({ opacity: 0, scale: 0.76 }, { opacity: 1, scale: 1 }, macFastTransition);
227
+ }
228
+
229
+ if (includesAny(macClassName, ["hd-mac-avatar", "hd-mac-avatar-image", "hd-mac-avatar-fallback"])) {
230
+ return fadeScaleDefaults({ opacity: 0, scale: 0.92 });
231
+ }
232
+
233
+ if (includesAny(macClassName, ["hd-mac-error-text", "hd-mac-help-text", "hd-mac-status-text", "hd-mac-validity"])) {
234
+ return fadeScaleDefaults({ opacity: 0, y: -3 }, { opacity: 1, y: 0 }, macFastTransition);
235
+ }
236
+
237
+ if (includesAny(macClassName, ["hd-mac-separator", "hd-mac-fieldset", "hd-mac-form"])) {
238
+ return {
239
+ initial: { opacity: 0 },
240
+ animate: { opacity: 1 },
241
+ transition: { duration: 0.12, ease: "easeOut" },
242
+ };
243
+ }
244
+
245
+ return {
246
+ layout: includesAny(macClassName, ["hd-mac-toggle-group", "hd-mac-tabs", "hd-mac-navigation-menu"]),
247
+ transition: macDefaultTransition,
248
+ };
249
+ }
250
+
57
251
  function macPart<C extends AnyComponent>(Component: C, macClassName: string) {
58
- const Wrapped = React.forwardRef<unknown, React.ComponentPropsWithoutRef<C>>((props, ref) => {
252
+ const BaseWrapped = React.forwardRef<unknown, React.ComponentPropsWithoutRef<C>>((props, ref) => {
59
253
  const TypedComponent = Component as AnyComponent;
60
254
  const { className, ...rest } = props as Record<string, unknown>;
61
255
  return React.createElement(TypedComponent, {
@@ -65,8 +259,21 @@ function macPart<C extends AnyComponent>(Component: C, macClassName: string) {
65
259
  });
66
260
  });
67
261
 
262
+ BaseWrapped.displayName = `MacBase${Component.displayName ?? Component.name ?? "BaseUIPart"}`;
263
+
264
+ const MotionPart = motion.create(BaseWrapped as AnyComponent);
265
+ const Wrapped = React.forwardRef<unknown, React.ComponentPropsWithoutRef<C>>((props, ref) => {
266
+ const rest = props as Record<string, unknown>;
267
+ const motionDefaults = getMotionDefaults(macClassName, rest);
268
+ return React.createElement(MotionPart as AnyComponent, {
269
+ ...motionDefaults,
270
+ ...rest,
271
+ ref,
272
+ });
273
+ });
274
+
68
275
  Wrapped.displayName = `Mac${Component.displayName ?? Component.name ?? "BaseUIPart"}`;
69
- return Wrapped as unknown as C;
276
+ return Wrapped as unknown as MacAnimatedComponent<C>;
70
277
  }
71
278
 
72
279
  function macProvider<C extends AnyComponent>(Component: C) {
@@ -24,6 +24,7 @@ import {
24
24
  MacMenu,
25
25
  MacMenubar,
26
26
  MacMeter,
27
+ MacMotionProvider,
27
28
  MacNavigationMenu,
28
29
  MacNumberField,
29
30
  MacOTPField,
@@ -76,7 +77,8 @@ export function MacBaseUIGallery() {
76
77
  return (
77
78
  <MacCSPProvider>
78
79
  <MacDirectionProvider direction="ltr">
79
- <div className="hd-mac hd-mac-gallery">
80
+ <MacMotionProvider>
81
+ <div className="hd-mac hd-mac-gallery">
80
82
  <GallerySection title="Buttons">
81
83
  <MacButton>Default</MacButton>
82
84
  <MacPrimaryButton>Continue</MacPrimaryButton>
@@ -421,7 +423,8 @@ export function MacBaseUIGallery() {
421
423
  <MacToast.Viewport />
422
424
  </MacToast.Provider>
423
425
  </GallerySection>
424
- </div>
426
+ </div>
427
+ </MacMotionProvider>
425
428
  </MacDirectionProvider>
426
429
  </MacCSPProvider>
427
430
  );