From a28b2d7752ea86239f0427ebf0879f8663c14910 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Fri, 2 Oct 2026 12:10:02 -0700 Subject: [PATCH 1/9] initial package framing for native-core --- .changeset/modern-native-core.md | 5 + AGENTS.md | 9 +- packages/agentic/native-core/README.md | 205 ++++++++++++++++++ packages/agentic/native-core/package.json | 73 +++++++ packages/agentic/native-core/src/index.ts | 1 + .../agentic/native-core/src/macos/index.ts | 1 + packages/agentic/native-core/src/win/index.ts | 1 + .../agentic/native-core/src/win32/index.ts | 1 + .../agentic/native-core/src/windows/index.ts | 1 + .../native-core/tests/entrypoints.test.mjs | 57 +++++ packages/agentic/native-core/tsconfig.json | 16 ++ tsconfig.json | 3 + yarn.lock | 8 + 13 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 .changeset/modern-native-core.md create mode 100644 packages/agentic/native-core/README.md create mode 100644 packages/agentic/native-core/package.json create mode 100644 packages/agentic/native-core/src/index.ts create mode 100644 packages/agentic/native-core/src/macos/index.ts create mode 100644 packages/agentic/native-core/src/win/index.ts create mode 100644 packages/agentic/native-core/src/win32/index.ts create mode 100644 packages/agentic/native-core/src/windows/index.ts create mode 100644 packages/agentic/native-core/tests/entrypoints.test.mjs create mode 100644 packages/agentic/native-core/tsconfig.json diff --git a/.changeset/modern-native-core.md b/.changeset/modern-native-core.md new file mode 100644 index 0000000000..918f484f9b --- /dev/null +++ b/.changeset/modern-native-core.md @@ -0,0 +1,5 @@ +--- +"@fluentui-react-native/native-core": minor +--- + +Initialize the native-core package with cross-platform, macOS, Windows, shared Windows/Win32, and Win32 entrypoints and a native component and TurboModule organization plan. diff --git a/AGENTS.md b/AGENTS.md index 7d8f922586..b2d3381a1c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,6 +86,7 @@ This is the **FluentUI React Native** repository, a monorepo containing React Na /win32/ - Win32-specific test app /component-generator/ - Tool to generate new components /packages/ - Core library packages + /agentic/native-core/ - Shared modern native components and TurboModules /components/ - UI component implementations (Button, Checkbox, Avatar, etc.) /native/ - Standalone native-backed component packages (Callout, etc.) /framework/ - Core theming and composition framework @@ -238,7 +239,8 @@ The composition framework uses precise types for better type safety: ### Component Development **Component Location**: Stable JavaScript components are in `/packages/components/`, standalone native-backed components are in -`/packages/native/`, and components under development are in `/packages/experimental/`. +`/packages/native/`, and components under development are in `/packages/experimental/`. New modern native components and +modules share `/packages/agentic/native-core/`; follow its [organization plan](packages/agentic/native-core/README.md). **Component Structure**: Each component typically has: @@ -281,6 +283,11 @@ native code (iOS/Android/Windows): - iOS/macOS: Include `.podspec` files - Must be added to FluentTester's Podfile (transitive dependencies aren't autolinked) +For new modern native components and modules, use `@fluentui-react-native/native-core` rather than creating another standalone +package. Its Windows components are Fabric-only; macOS components support Paper and Fabric initially. Export shared APIs +from the root and scope platform APIs to `macos`, `windows` (React Native Windows), `win` (Windows/Win32), or `win32`. +Existing native packages are not migrated by this convention. + ### Creating a New Component 1. Create the package under `/packages/components/`, `/packages/native/` for a standalone diff --git a/packages/agentic/native-core/README.md b/packages/agentic/native-core/README.md new file mode 100644 index 0000000000..5816756caf --- /dev/null +++ b/packages/agentic/native-core/README.md @@ -0,0 +1,205 @@ +# Native Core + +`@fluentui-react-native/native-core` is the shared home for new native-backed +components and non-UI native modules. JavaScript should generally be a thin, +typed wrapper around native behavior, not a second implementation of it. +Higher-order Fluent components, theming, and composition remain in their +existing packages and can consume this package without introducing a reverse +dependency. + +## Initialization status + +The workspace, TypeScript build, package export map, and entrypoint contract +tests are initialized. All five entrypoints are intentionally empty. There are +no native implementations, codegen specs, native projects, or autolinking +registrations yet. This does not migrate Callout, FocusZone, or any other +existing package. + +The initial shell has no runtime dependencies. Add React Native/React peers, +optional platform-fork peers, development dependencies, and matching `rnx-kit` +capabilities with the first implementation, using the repository's supported +versions. Do not imply native compatibility from an empty entrypoint. + +## Public entrypoints + +| Import | Scope | Source entrypoint | +| -------------------------------------------- | --------------------------------------- | ---------------------- | +| `@fluentui-react-native/native-core` | Cross-platform APIs | `src/index.ts` | +| `@fluentui-react-native/native-core/macos` | macOS-specific APIs | `src/macos/index.ts` | +| `@fluentui-react-native/native-core/windows` | React Native Windows-specific APIs | `src/windows/index.ts` | +| `@fluentui-react-native/native-core/win` | APIs shared by Windows and Win32 | `src/win/index.ts` | +| `@fluentui-react-native/native-core/win32` | Office React Native Win32-specific APIs | `src/win32/index.ts` | + +Each entrypoint explicitly exports components, module facades, and their +public types. The root is not an umbrella export of the platform submodules; +`win` is not an umbrella export of `windows` and `win32`. Give an API its +narrowest applicable public home. Shared implementation does not itself make +an API cross-platform. + +Root exports require equivalent implementations on all supported desktop +hosts: macOS, React Native Windows, and Win32. Additional platform support +must be stated and verified per API; this initialization does not promise +iOS or Android support. A `win` export must work on both Windows hosts, +despite their potentially different native implementations. + +Keep platform imports behind the corresponding entrypoint and implementation +file. Importing one entrypoint must not initialize another platform's native +components or module registry. Keep schemas, generated bindings, registration, +and internal helpers private; the export map deliberately disallows deep +imports. Never use wildcard re-exports. + +## JavaScript and specification layout + +Only the entrypoint files exist initially. Add the following directories as +real features arrive rather than filling them with placeholder native APIs: + +```text +src/ + index.ts + macos/index.ts + windows/index.ts + win/index.ts + win32/index.ts + components/ + / + .tsx Thin public wrapper, when shared + .types.ts Public props, events, commands, and refs + ..tsx Platform binding when needed + .test.tsx Wrapper behavior tests + .types.test.ts Public type contract + SPEC.md Supported platforms and renderer behavior + modules/ + / + .ts Typed public facade over a native module + ..ts Platform binding when needed + .test.ts Facade and error propagation tests + SPEC.md Availability, threading, events, and lifetime + specs/ + components/ + NativeComponent.ts Fabric props, events, and native commands + modules/ + Native.ts TurboModule interface + internal/ Private shared boundary utilities, as needed +``` + +Use React Native codegen-compatible schemas as the JS/native source of truth. +Keep public ergonomic props separate from the lower-level native schema only +when translation is necessary. Native component wrappers forward props, +events, commands, children, and native refs; module facades normalize only +the behavior needed for the public contract. + +Use `.macos`, `.windows`, and `.win32` implementation files for real host +differences. `win` is a package scope, not a Metro platform extension. Keep +React Native fork imports out of the shared TypeScript type graph; use +platform-specific files or platform-neutral shapes. Missing required native +registrations must surface a descriptive error, not a no-op component or a +success-shaped module fallback. + +## Native layout and architecture + +The planned native layout separates UI components from non-UI modules and +shares platform behavior below the renderer adapters: + +```text +macos/ + components// + shared/ AppKit view and behavior + fabric/ Fabric component-view adapter + paper/ Temporary Paper view-manager adapter + modules// TurboModule implementation and required glue + shared/ Platform utilities used by multiple features + registration/ Component and module provider wiring +windows/ + NativeCore/ + components// Fabric component views + modules// TurboModule implementations + shared/ C++/WinRT platform utilities + registration/ ReactPackageProvider wiring + codegen/ Generated Windows bindings, never hand-edited +win32/ Host-specific integration, only when needed +FRNNativeCore.podspec macOS sources and native dependencies +react-native.config.cjs Native project discovery/autolinking +``` + +| Host | Native component policy | Non-UI module policy | +| -------------------------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | +| React Native Windows (`windows`) | Fabric only; no new Paper view managers or fallback path | TurboModules, with host support verified per module | +| macOS (`macos`) | Both Paper and Fabric initially; Fabric-only long term | TurboModules; add narrowly scoped legacy-host glue only when required and verified | +| Office Win32 (`win32`) | Explicit host contract per feature; do not assume the RNW Fabric implementation is compatible | Explicit host registration and module availability per feature | + +On macOS, Paper and Fabric adapters must expose the same props, events, +commands, refs, and behavior while delegating to shared AppKit logic. +Keep Paper-specific code in `paper/` and guard native compilation with the +host's architecture configuration. Retire that adapter after supported macOS +hosts become Fabric-only, without changing the public JS API. + +The Windows component project must require the New Architecture and fail +clearly for a Paper build. Use the repository's +[Windows Fabric native component reference](../../../.github/skills/agentic-component-authoring/references/windows-fabric-native-components.md) +and verify APIs against the installed RNW version. +Win32 integration is separate from React Native Windows Paper support; adding +a Win32 export does not authorize a second RNW view-manager implementation. + +Component renderer support and TurboModule availability are separate +capabilities. Do not infer module availability solely from Fabric being +enabled. Modules own their native threading, resource cleanup, event +subscriptions, and cancellation contracts; wrappers preserve those contracts +and expose native failures. + +## Codegen, registration, and publishing plan + +Introduce native build metadata with the first real schema and native target: + +1. Use one package-level codegen identity, `FRNNativeCoreSpec`, with + `type: "all"` and `jsSrcsDir: "src/specs"` so components and modules remain + distinct in source but share a generation boundary. +2. Name component schemas `NativeComponent.ts` and module schemas + `Native.ts`. Prefix native registration names with `FRNNativeCore` + to avoid collisions with existing packages; keep each name identical in + the schema, generated interface, native registration, and JS binding. +3. Configure Windows with both `componentsWindows` and `modulesWindows` + generators and `windows/NativeCore/codegen` output. Apple codegen should + run through the consuming macOS app's Pod integration. Inspect each + platform's generated schema and provider list so platform-only APIs do + not require registration on unrelated hosts. +4. Leave `includesGeneratedCode` unset unless generated artifacts for every + supported native platform are deliberately shipped and verified. It is + not a Windows-only switch and can prevent Apple code generation. +5. Add the macOS podspec, Windows project/provider, and autolinking metadata + only when they refer to real sources. Exclude Paper adapters from macOS + Fabric compilation and Fabric adapters from Paper compilation. Register + only implementations supported by the active host/architecture. +6. Keep native targets and required generated outputs in the published + archive. The initial `files` allowlist reserves `src`, `lib`, `macos`, + `windows`, `win32`, podspecs, and `react-native.config.cjs`. Intermediate + native build artifacts, tests, and machine-local project output must not + leak into it. +7. Verify the packed package in consuming hosts before replacing an existing + package. Migrations are separate changes with compatibility evidence, not + implicit effects of adding this workspace. + +## Delivery and validation + +Add each feature as one reviewed contract, thin wrapper/schema, native +implementation, explicit export, and tests. Start with one representative +Fabric component and one TurboModule to exercise both codegen paths. +macOS component admission requires both Paper and Fabric evidence; Windows +component admission requires Fabric evidence. A `win` or root API also +requires Win32 evidence before it is exported there. + +Run the owning workspace's scripts from the repository root: + +```sh +yarn workspace @fluentui-react-native/native-core format --check +yarn workspace @fluentui-react-native/native-core lint +yarn workspace @fluentui-react-native/native-core build +yarn workspace @fluentui-react-native/native-core test +yarn build +``` + +The initial Node tests validate all five export maps, emitted JS/declaration +files, and conditional resolution for compiled, React Native source, and +TypeScript entrypoints. Add the repository's shared Jest configuration and +native integration tests when wrappers and native targets exist. Native +verification must cover registration, events/commands, refs, unmount cleanup, +error propagation, and architecture/platform parity, not only JS mocks. diff --git a/packages/agentic/native-core/package.json b/packages/agentic/native-core/package.json new file mode 100644 index 0000000000..6542e731eb --- /dev/null +++ b/packages/agentic/native-core/package.json @@ -0,0 +1,73 @@ +{ + "name": "@fluentui-react-native/native-core", + "version": "0.0.1", + "description": "Modern native components and modules for Fluent UI React Native.", + "license": "MIT", + "author": "", + "repository": { + "type": "git", + "url": "https://github.com/microsoft/fluentui-react-native.git", + "directory": "packages/agentic/native-core" + }, + "files": [ + "lib", + "src", + "macos", + "windows", + "win32", + "*.podspec", + "react-native.config.cjs" + ], + "type": "module", + "sideEffects": false, + "main": "lib/index.js", + "module": "lib/index.js", + "types": "lib/index.d.ts", + "exports": { + ".": { + "types": "./lib/index.d.ts", + "react-native": "./src/index.ts", + "import": "./lib/index.js", + "default": "./src/index.ts" + }, + "./macos": { + "types": "./lib/macos/index.d.ts", + "react-native": "./src/macos/index.ts", + "import": "./lib/macos/index.js", + "default": "./src/macos/index.ts" + }, + "./windows": { + "types": "./lib/windows/index.d.ts", + "react-native": "./src/windows/index.ts", + "import": "./lib/windows/index.js", + "default": "./src/windows/index.ts" + }, + "./win": { + "types": "./lib/win/index.d.ts", + "react-native": "./src/win/index.ts", + "import": "./lib/win/index.js", + "default": "./src/win/index.ts" + }, + "./win32": { + "types": "./lib/win32/index.d.ts", + "react-native": "./src/win32/index.ts", + "import": "./lib/win32/index.js", + "default": "./src/win32/index.ts" + }, + "./package.json": "./package.json" + }, + "scripts": { + "build": "tsc -b", + "clean": "fluentui-scripts clean", + "format": "fluentui-scripts format", + "lint": "fluentui-scripts lint", + "test": "yarn build && node --test tests/entrypoints.test.mjs" + }, + "devDependencies": { + "@fluentui-react-native/scripts": "workspace:*" + }, + "rnx-kit": { + "kitType": "library", + "extends": "@fluentui-react-native/scripts/kit-config" + } +} diff --git a/packages/agentic/native-core/src/index.ts b/packages/agentic/native-core/src/index.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/packages/agentic/native-core/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/agentic/native-core/src/macos/index.ts b/packages/agentic/native-core/src/macos/index.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/packages/agentic/native-core/src/macos/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/agentic/native-core/src/win/index.ts b/packages/agentic/native-core/src/win/index.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/packages/agentic/native-core/src/win/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/agentic/native-core/src/win32/index.ts b/packages/agentic/native-core/src/win32/index.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/packages/agentic/native-core/src/win32/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/agentic/native-core/src/windows/index.ts b/packages/agentic/native-core/src/windows/index.ts new file mode 100644 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/packages/agentic/native-core/src/windows/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/agentic/native-core/tests/entrypoints.test.mjs b/packages/agentic/native-core/tests/entrypoints.test.mjs new file mode 100644 index 0000000000..3fda319b2f --- /dev/null +++ b/packages/agentic/native-core/tests/entrypoints.test.mjs @@ -0,0 +1,57 @@ +import assert from 'node:assert/strict'; +import { spawnSync } from 'node:child_process'; +import { access, readFile } from 'node:fs/promises'; +import test from 'node:test'; +import { fileURLToPath } from 'node:url'; + +const packageRoot = new URL('../', import.meta.url); +const manifest = JSON.parse(await readFile(new URL('package.json', packageRoot), 'utf8')); +const entrypoints = [ + ['.', 'index'], + ['./macos', 'macos/index'], + ['./windows', 'windows/index'], + ['./win', 'win/index'], + ['./win32', 'win32/index'], +]; + +test('declares exactly the five public entrypoints and package metadata', () => { + assert.equal(manifest.name, '@fluentui-react-native/native-core'); + assert.deepEqual(Object.keys(manifest.exports), [...entrypoints.map(([entrypoint]) => entrypoint), './package.json']); + assert.equal(manifest.exports['./package.json'], './package.json'); + assert.equal(manifest.main, 'lib/index.js'); + assert.equal(manifest.module, 'lib/index.js'); + assert.equal(manifest.types, 'lib/index.d.ts'); +}); + +for (const [entrypoint, path] of entrypoints) { + test(`${entrypoint} has distinct source, JavaScript, and declaration targets`, async () => { + const expected = { + types: `./lib/${path}.d.ts`, + 'react-native': `./src/${path}.ts`, + import: `./lib/${path}.js`, + default: `./src/${path}.ts`, + }; + assert.deepEqual(manifest.exports[entrypoint], expected); + assert.deepEqual(Object.keys(manifest.exports[entrypoint]), Object.keys(expected)); + await Promise.all(Object.values(expected).map((target) => access(new URL(target, packageRoot)))); + }); +} + +for (const condition of ['import', 'react-native', 'types']) { + test(`${condition} resolution selects the correct public entrypoints`, () => { + const specifiers = entrypoints.map(([entrypoint]) => manifest.name + (entrypoint === '.' ? '' : entrypoint.slice(1))); + const script = `console.log(JSON.stringify(${JSON.stringify(specifiers)}.map((specifier) => import.meta.resolve(specifier))))`; + const args = condition === 'import' ? [] : [`--conditions=${condition}`]; + const result = spawnSync(process.execPath, [...args, '--input-type=module', '--eval', script], { + cwd: fileURLToPath(packageRoot), + encoding: 'utf8', + }); + + assert.ifError(result.error); + assert.equal(result.status, 0, result.stderr); + assert.deepEqual( + JSON.parse(result.stdout), + entrypoints.map(([entrypoint]) => new URL(manifest.exports[entrypoint][condition], packageRoot).href), + ); + }); +} diff --git a/packages/agentic/native-core/tsconfig.json b/packages/agentic/native-core/tsconfig.json new file mode 100644 index 0000000000..d952200ef0 --- /dev/null +++ b/packages/agentic/native-core/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "@fluentui-react-native/scripts/tsconfig", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src", + "composite": true, + "tsBuildInfoFile": ".cache/tsconfig.tsbuildinfo" + }, + "include": ["src"], + "exclude": ["**/*.stories.ts", "**/*.stories.tsx", "**/*.wdio.ts"], + "references": [ + { + "path": "../../../scripts/tsconfig.json" + } + ] +} diff --git a/tsconfig.json b/tsconfig.json index d2f500b851..2d951847c5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,6 +28,9 @@ { "path": "packages/agentic/desktop-driver/tsconfig.json" }, + { + "path": "packages/agentic/native-core/tsconfig.json" + }, { "path": "packages/agentic/storybook-desktop/tsconfig.json" }, diff --git a/yarn.lock b/yarn.lock index 3fa2fa59ba..9357564ab9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4515,6 +4515,14 @@ __metadata: languageName: unknown linkType: soft +"@fluentui-react-native/native-core@workspace:packages/agentic/native-core": + version: 0.0.0-use.local + resolution: "@fluentui-react-native/native-core@workspace:packages/agentic/native-core" + dependencies: + "@fluentui-react-native/scripts": "workspace:*" + languageName: unknown + linkType: soft + "@fluentui-react-native/notification@workspace:*, @fluentui-react-native/notification@workspace:packages/components/Notification": version: 0.0.0-use.local resolution: "@fluentui-react-native/notification@workspace:packages/components/Notification" From 4c184851504478567bf5f5221a0b72fec5617170 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Fri, 2 Oct 2026 13:06:16 -0700 Subject: [PATCH 2/9] move callout and focus-zone into native-core package --- .changeset/modern-native-core.md | 2 + .changeset/native-core-component-migration.md | 9 ++ .../references/focus-macos.md | 16 +- .../references/focus-windows.md | 4 +- .gitignore | 6 + .oxfmtrc.json | 2 +- AGENTS.md | 20 +-- apps/E2E/tsconfig.json | 2 +- apps/fluent-tester/package.json | 1 + apps/fluent-tester/tsconfig.json | 7 +- apps/storybook/README.md | 14 +- apps/storybook/agent-map.yaml | 2 +- apps/storybook/package.json | 6 +- apps/storybook/storybook.config.mts | 2 +- apps/storybook/tsconfig.json | 11 +- apps/tester-core/tsconfig.json | 4 +- .../agentic/components/WIN32-FOCUS-PLAN.md | 18 +-- packages/agentic/components/package.json | 7 +- .../focus-zone/focus-zone.stories.tsx | 4 +- packages/agentic/components/tsconfig.json | 11 +- .../native-core/FRNNativeCore.podspec} | 6 +- .../native-core}/NuGet.config | 0 packages/agentic/native-core/README.md | 70 ++++---- .../native-core}/jest.config.cjs | 0 .../Callout/fabric}/RCTCalloutComponentView.h | 0 .../fabric}/RCTCalloutComponentView.mm | 10 +- .../Callout/paper}/CalloutManager.swift | 0 .../Callout/paper}/FRNCalloutManager.h | 0 .../Callout/paper}/FRNCalloutManager.m | 2 +- .../Callout/shared}/CalloutView.swift | 0 .../Callout/shared}/CalloutWindow.swift | 0 .../components/Callout/shared}/FRNCallout.h | 2 +- .../shared}/FlippedVisualEffectView.swift | 0 .../Callout/shared}/GuardedEventMonitor.swift | 0 .../fabric}/RCTFocusZoneComponentView.h | 0 .../fabric}/RCTFocusZoneComponentView.mm | 10 +- .../FocusZone/paper}/RCTFocusZoneManager.h | 0 .../FocusZone/paper}/RCTFocusZoneManager.m | 2 +- .../components/FocusZone/shared}/KeyCodes.h | 0 .../FocusZone/shared}/RCTFocusZone.h | 0 .../FocusZone/shared}/RCTFocusZone.m | 0 packages/agentic/native-core/package.json | 107 +++++++++++- .../native-core}/react-native.config.cjs | 2 +- .../components/callout}/Callout.stories.tsx | 0 .../src/components/callout}/Callout.tsx | 2 +- .../src/components/callout}/Callout.types.ts | 0 .../src/components/callout}/SPEC.md | 12 +- .../callout}/__tests__/Callout.test.tsx | 2 +- .../callout}/__tests__/Callout.types.test.ts | 0 .../__snapshots__/Callout.test.tsx.snap | 0 .../src/components/focus-zone}/FocusZone.tsx | 2 +- .../components/focus-zone}/FocusZone.types.ts | 0 .../src/components/focus-zone}/SPEC.md | 11 +- .../focus-zone}/__tests__/FocusZone.test.tsx | 0 .../__tests__/FocusZone.types.test.ts | 0 packages/agentic/native-core/src/index.ts | 28 +++- .../components}/CalloutNativeComponent.ts | 0 .../components}/FocusZoneNativeComponent.ts | 0 .../FocusZoneNativeComponent.win32.ts | 0 .../tests/native-ownership.test.mjs | 63 ++++++++ packages/agentic/native-core/tsconfig.json | 6 + .../native-core}/windows/.gitignore | 0 .../windows/ExperimentalFeatures.props | 0 .../windows/NativeCore/NativeCore.def} | 0 .../windows/NativeCore/NativeCore.vcxproj} | 16 +- .../NativeCore}/ReactPackageProvider.cpp | 9 +- .../NativeCore}/ReactPackageProvider.h | 8 +- .../NativeCore}/ReactPackageProvider.idl | 2 +- .../windows/NativeCore}/codegen/.clang-format | 2 +- .../components/FRNNativeCoreSpec}/Callout.g.h | 4 +- .../FRNNativeCoreSpec}/FocusZone.g.h | 4 +- .../components}/Callout/Callout.cpp | 0 .../NativeCore/components}/Callout/Callout.h | 4 +- .../FocusZone}/FocusZoneComponentView.cpp | 6 +- .../FocusZone}/FocusZoneComponentView.h | 0 .../windows/NativeCore}/packages.lock.json | 0 .../native-core/windows/NativeCore}/pch.cpp | 0 .../native-core/windows/NativeCore}/pch.h | 0 .../windows/NativeCore}/targetver.h | 0 .../native-core}/windows/NuGet.Config | 0 .../storybook-desktop-runtime/package.json | 4 +- .../src/Win32CalloutPortal.tsx | 4 +- .../storybook-desktop-runtime/tsconfig.json | 6 +- .../config/makeDesktopStorybookConfig.test.ts | 10 +- .../components/ContextualMenu/tsconfig.json | 4 +- .../components/FocusZone/RCTFocusZone.podspec | 21 --- .../FocusZone/react-native.config.cjs | 15 -- .../FocusZone/windows/FRNFocusZone.sln | 43 ----- .../windows/FRNFocusZone/FRNFocusZone.rc | Bin 160 -> 0 bytes .../FRNFocusZone/FRNFocusZone.vcxproj.filters | 44 ----- .../FocusZone/windows/FRNFocusZone/resource.h | 5 - packages/components/Menu/tsconfig.json | 4 +- packages/components/RadioGroup/tsconfig.json | 2 +- packages/components/TabList/tsconfig.json | 2 +- .../deprecated/foundation-compose/AGENTS.md | 2 +- packages/experimental/Dropdown/tsconfig.json | 2 +- packages/experimental/Tooltip/tsconfig.json | 2 +- packages/libraries/core/tsconfig.json | 4 +- packages/native/Callout/windows/.gitignore | 46 ------ .../Callout/windows/Callout/Callout.def | 3 - .../Callout/windows/Callout/Callout.vcxproj | 152 ------------------ .../windows/Callout/ReactPackageProvider.cpp | 25 --- .../windows/Callout/ReactPackageProvider.h | 25 --- .../windows/Callout/ReactPackageProvider.idl | 12 -- .../windows/Callout/codegen/.clang-format | 2 - .../windows/Callout/packages.lock.json | 151 ----------------- .../native/Callout/windows/Callout/pch.cpp | 1 - packages/native/Callout/windows/Callout/pch.h | 28 ---- .../windows/ExperimentalFeatures.props | 33 ---- packages/native/Callout/windows/NuGet.Config | 13 -- .../Callout => shim/callout}/CHANGELOG.json | 0 .../Callout => shim/callout}/CHANGELOG.md | 0 packages/shim/callout/README.md | 18 +++ .../Callout => shim/callout}/jest.config.cjs | 0 .../Callout => shim/callout}/package.json | 37 ++--- packages/shim/callout/src/index.test.ts | 22 +++ .../Callout => shim/callout}/src/index.ts | 5 +- .../Callout => shim/callout}/tsconfig.json | 2 +- .../focus-zone}/CHANGELOG.json | 0 .../focus-zone}/CHANGELOG.md | 0 packages/shim/focus-zone/README.md | 19 +++ packages/shim/focus-zone/jest.config.cjs | 1 + .../focus-zone}/package.json | 39 ++--- packages/shim/focus-zone/src/index.test.ts | 26 +++ .../focus-zone}/src/index.ts | 7 +- .../focus-zone}/tsconfig.json | 8 +- tsconfig.json | 8 +- yarn.lock | 62 +++++-- 128 files changed, 578 insertions(+), 881 deletions(-) create mode 100644 .changeset/native-core-component-migration.md rename packages/{native/Callout/FRNCallout.podspec => agentic/native-core/FRNNativeCore.podspec} (82%) rename packages/{native/Callout => agentic/native-core}/NuGet.config (100%) rename packages/{components/FocusZone => agentic/native-core}/jest.config.cjs (100%) rename packages/{native/Callout/macos => agentic/native-core/macos/components/Callout/fabric}/RCTCalloutComponentView.h (100%) rename packages/{native/Callout/macos => agentic/native-core/macos/components/Callout/fabric}/RCTCalloutComponentView.mm (95%) rename packages/{native/Callout/macos => agentic/native-core/macos/components/Callout/paper}/CalloutManager.swift (100%) rename packages/{native/Callout/macos => agentic/native-core/macos/components/Callout/paper}/FRNCalloutManager.h (100%) rename packages/{native/Callout/macos => agentic/native-core/macos/components/Callout/paper}/FRNCalloutManager.m (98%) rename packages/{native/Callout/macos => agentic/native-core/macos/components/Callout/shared}/CalloutView.swift (100%) rename packages/{native/Callout/macos => agentic/native-core/macos/components/Callout/shared}/CalloutWindow.swift (100%) rename packages/{native/Callout/macos => agentic/native-core/macos/components/Callout/shared}/FRNCallout.h (57%) rename packages/{native/Callout/macos => agentic/native-core/macos/components/Callout/shared}/FlippedVisualEffectView.swift (100%) rename packages/{native/Callout/macos => agentic/native-core/macos/components/Callout/shared}/GuardedEventMonitor.swift (100%) rename packages/{components/FocusZone/macos => agentic/native-core/macos/components/FocusZone/fabric}/RCTFocusZoneComponentView.h (100%) rename packages/{components/FocusZone/macos => agentic/native-core/macos/components/FocusZone/fabric}/RCTFocusZoneComponentView.mm (94%) rename packages/{components/FocusZone/macos => agentic/native-core/macos/components/FocusZone/paper}/RCTFocusZoneManager.h (100%) rename packages/{components/FocusZone/macos => agentic/native-core/macos/components/FocusZone/paper}/RCTFocusZoneManager.m (97%) rename packages/{components/FocusZone/macos => agentic/native-core/macos/components/FocusZone/shared}/KeyCodes.h (100%) rename packages/{components/FocusZone/macos => agentic/native-core/macos/components/FocusZone/shared}/RCTFocusZone.h (100%) rename packages/{components/FocusZone/macos => agentic/native-core/macos/components/FocusZone/shared}/RCTFocusZone.m (100%) rename packages/{native/Callout => agentic/native-core}/react-native.config.cjs (78%) rename packages/{native/Callout/src => agentic/native-core/src/components/callout}/Callout.stories.tsx (100%) rename packages/{native/Callout/src => agentic/native-core/src/components/callout}/Callout.tsx (97%) rename packages/{native/Callout/src => agentic/native-core/src/components/callout}/Callout.types.ts (100%) rename packages/{native/Callout => agentic/native-core/src/components/callout}/SPEC.md (77%) rename packages/{native/Callout/src => agentic/native-core/src/components/callout}/__tests__/Callout.test.tsx (98%) rename packages/{native/Callout/src => agentic/native-core/src/components/callout}/__tests__/Callout.types.test.ts (100%) rename packages/{native/Callout/src => agentic/native-core/src/components/callout}/__tests__/__snapshots__/Callout.test.tsx.snap (100%) rename packages/{components/FocusZone/src => agentic/native-core/src/components/focus-zone}/FocusZone.tsx (96%) rename packages/{components/FocusZone/src => agentic/native-core/src/components/focus-zone}/FocusZone.types.ts (100%) rename packages/{components/FocusZone => agentic/native-core/src/components/focus-zone}/SPEC.md (83%) rename packages/{components/FocusZone/src => agentic/native-core/src/components/focus-zone}/__tests__/FocusZone.test.tsx (100%) rename packages/{components/FocusZone/src => agentic/native-core/src/components/focus-zone}/__tests__/FocusZone.types.test.ts (100%) rename packages/{native/Callout/src => agentic/native-core/src/specs/components}/CalloutNativeComponent.ts (100%) rename packages/{components/FocusZone/src => agentic/native-core/src/specs/components}/FocusZoneNativeComponent.ts (100%) rename packages/{components/FocusZone/src => agentic/native-core/src/specs/components}/FocusZoneNativeComponent.win32.ts (100%) create mode 100644 packages/agentic/native-core/tests/native-ownership.test.mjs rename packages/{components/FocusZone => agentic/native-core}/windows/.gitignore (100%) rename packages/{components/FocusZone => agentic/native-core}/windows/ExperimentalFeatures.props (100%) rename packages/{components/FocusZone/windows/FRNFocusZone/FRNFocusZone.def => agentic/native-core/windows/NativeCore/NativeCore.def} (100%) rename packages/{components/FocusZone/windows/FRNFocusZone/FRNFocusZone.vcxproj => agentic/native-core/windows/NativeCore/NativeCore.vcxproj} (94%) rename packages/{components/FocusZone/windows/FRNFocusZone => agentic/native-core/windows/NativeCore}/ReactPackageProvider.cpp (59%) rename packages/{components/FocusZone/windows/FRNFocusZone => agentic/native-core/windows/NativeCore}/ReactPackageProvider.h (65%) rename packages/{components/FocusZone/windows/FRNFocusZone => agentic/native-core/windows/NativeCore}/ReactPackageProvider.idl (87%) rename packages/{components/FocusZone/windows/FRNFocusZone => agentic/native-core/windows/NativeCore}/codegen/.clang-format (50%) rename packages/{native/Callout/windows/Callout/codegen/react/components/FRNCalloutSpec => agentic/native-core/windows/NativeCore/codegen/react/components/FRNNativeCoreSpec}/Callout.g.h (99%) rename packages/{components/FocusZone/windows/FRNFocusZone/codegen/react/components/FRNFocusZoneSpec => agentic/native-core/windows/NativeCore/codegen/react/components/FRNNativeCoreSpec}/FocusZone.g.h (99%) rename packages/{native/Callout/windows => agentic/native-core/windows/NativeCore/components}/Callout/Callout.cpp (100%) rename packages/{native/Callout/windows => agentic/native-core/windows/NativeCore/components}/Callout/Callout.h (86%) rename packages/{components/FocusZone/windows/FRNFocusZone => agentic/native-core/windows/NativeCore/components/FocusZone}/FocusZoneComponentView.cpp (98%) rename packages/{components/FocusZone/windows/FRNFocusZone => agentic/native-core/windows/NativeCore/components/FocusZone}/FocusZoneComponentView.h (100%) rename packages/{components/FocusZone/windows/FRNFocusZone => agentic/native-core/windows/NativeCore}/packages.lock.json (100%) rename packages/{components/FocusZone/windows/FRNFocusZone => agentic/native-core/windows/NativeCore}/pch.cpp (100%) rename packages/{components/FocusZone/windows/FRNFocusZone => agentic/native-core/windows/NativeCore}/pch.h (100%) rename packages/{components/FocusZone/windows/FRNFocusZone => agentic/native-core/windows/NativeCore}/targetver.h (100%) rename packages/{components/FocusZone => agentic/native-core}/windows/NuGet.Config (100%) delete mode 100644 packages/components/FocusZone/RCTFocusZone.podspec delete mode 100644 packages/components/FocusZone/react-native.config.cjs delete mode 100644 packages/components/FocusZone/windows/FRNFocusZone.sln delete mode 100644 packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.rc delete mode 100644 packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.vcxproj.filters delete mode 100644 packages/components/FocusZone/windows/FRNFocusZone/resource.h delete mode 100644 packages/native/Callout/windows/.gitignore delete mode 100644 packages/native/Callout/windows/Callout/Callout.def delete mode 100644 packages/native/Callout/windows/Callout/Callout.vcxproj delete mode 100644 packages/native/Callout/windows/Callout/ReactPackageProvider.cpp delete mode 100644 packages/native/Callout/windows/Callout/ReactPackageProvider.h delete mode 100644 packages/native/Callout/windows/Callout/ReactPackageProvider.idl delete mode 100644 packages/native/Callout/windows/Callout/codegen/.clang-format delete mode 100644 packages/native/Callout/windows/Callout/packages.lock.json delete mode 100644 packages/native/Callout/windows/Callout/pch.cpp delete mode 100644 packages/native/Callout/windows/Callout/pch.h delete mode 100644 packages/native/Callout/windows/ExperimentalFeatures.props delete mode 100644 packages/native/Callout/windows/NuGet.Config rename packages/{native/Callout => shim/callout}/CHANGELOG.json (100%) rename packages/{native/Callout => shim/callout}/CHANGELOG.md (100%) create mode 100644 packages/shim/callout/README.md rename packages/{native/Callout => shim/callout}/jest.config.cjs (100%) rename packages/{native/Callout => shim/callout}/package.json (77%) create mode 100644 packages/shim/callout/src/index.test.ts rename packages/{native/Callout => shim/callout}/src/index.ts (60%) rename packages/{native/Callout => shim/callout}/tsconfig.json (87%) rename packages/{components/FocusZone => shim/focus-zone}/CHANGELOG.json (100%) rename packages/{components/FocusZone => shim/focus-zone}/CHANGELOG.md (100%) create mode 100644 packages/shim/focus-zone/README.md create mode 100644 packages/shim/focus-zone/jest.config.cjs rename packages/{components/FocusZone => shim/focus-zone}/package.json (74%) create mode 100644 packages/shim/focus-zone/src/index.test.ts rename packages/{components/FocusZone => shim/focus-zone}/src/index.ts (54%) rename packages/{components/FocusZone => shim/focus-zone}/tsconfig.json (65%) diff --git a/.changeset/modern-native-core.md b/.changeset/modern-native-core.md index 918f484f9b..e3bb7476d2 100644 --- a/.changeset/modern-native-core.md +++ b/.changeset/modern-native-core.md @@ -3,3 +3,5 @@ --- Initialize the native-core package with cross-platform, macOS, Windows, shared Windows/Win32, and Win32 entrypoints and a native component and TurboModule organization plan. + +Own the Callout and FocusZone JS wrappers, codegen specifications, macOS Paper/Fabric adapters, and Windows Fabric implementations in this shared package. diff --git a/.changeset/native-core-component-migration.md b/.changeset/native-core-component-migration.md new file mode 100644 index 0000000000..a4a72c2b51 --- /dev/null +++ b/.changeset/native-core-component-migration.md @@ -0,0 +1,9 @@ +--- +"@fluentui-react-native/callout": patch +"@fluentui-react-native/focus-zone": patch +"@fluentui-react-native/components": patch +"@fluentui-react-native/storybook-desktop-runtime": patch +"@fluentui-react-native/tester": patch +--- + +Move Callout and FocusZone implementations into native-core and retain the existing packages as JS-only compatibility shims under packages/shim. Native applications must directly depend on native-core and regenerate native autolinking/Pods; macOS now uses the shared FRNNativeCore pod and Windows uses one Fabric library for both components. Public APIs and native registration names are unchanged. diff --git a/.github/skills/agentic-component-authoring/references/focus-macos.md b/.github/skills/agentic-component-authoring/references/focus-macos.md index 1e9cf6184c..990010bb1a 100644 --- a/.github/skills/agentic-component-authoring/references/focus-macos.md +++ b/.github/skills/agentic-component-authoring/references/focus-macos.md @@ -31,8 +31,8 @@ focus visual should be presented as active. exposure or global numeric-tag lookup. Local references: -`packages\components\FocusZone\macos\RCTFocusZone.m:24-35,122-129`; -`packages\components\FocusZone\macos\RCTFocusZoneComponentView.mm:118-179`. +`packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:24-35,122-129`; +`packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-179`. Installed RNmacOS `React\Fabric\Mounting\ComponentViews\View\RCTViewComponentView.mm:1719-1804` implements focus/blur commands and responder event emission. @@ -105,8 +105,8 @@ same keys. Recompute eligibility when children mount/unmount and when props change, and clear the default responder on recycle. Retain direction, wrap/stop, RTL, nested zones, scrolling, and editable-child semantics. -Evidence: `packages\components\FocusZone\macos\RCTFocusZone.m:584-675`; -`packages\components\FocusZone\macos\RCTFocusZoneComponentView.mm:118-195`. +Evidence: `packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:584-675`; +`packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-195`. ## Popup windows and lifecycle @@ -131,10 +131,10 @@ Do not install OS-global keyboard hooks or require desktop-driver permissions for ordinary production focus behavior. Evidence: -`packages\native\Callout\macos\CalloutWindow.swift:9-38`; -`packages\native\Callout\macos\CalloutView.swift:33-45,142-216`; -`packages\native\Callout\macos\RCTCalloutComponentView.mm:96-132`; -`packages\native\Callout\macos\GuardedEventMonitor.swift:1-29`; +`packages\agentic\native-core\macos\components\Callout\shared\CalloutWindow.swift:9-38`; +`packages\agentic\native-core\macos\components\Callout\shared\CalloutView.swift:33-45,142-216`; +`packages\agentic\native-core\macos\components\Callout\fabric\RCTCalloutComponentView.mm:96-132`; +`packages\agentic\native-core\macos\components\Callout\shared\GuardedEventMonitor.swift:1-29`; `packages\components\ContextualMenu\src\ContextualMenu.tsx:39-55`. ## Native versus custom ring drawing diff --git a/.github/skills/agentic-component-authoring/references/focus-windows.md b/.github/skills/agentic-component-authoring/references/focus-windows.md index bcf96e3311..bd677f65cc 100644 --- a/.github/skills/agentic-component-authoring/references/focus-windows.md +++ b/.github/skills/agentic-component-authoring/references/focus-windows.md @@ -53,7 +53,7 @@ assume a React ref can be serialized into a TurboModule argument. The existing Fabric FocusZone uses `TryFocus(FocusState::Keyboard)` and checks the root's focused component after the request -(`packages\components\FocusZone\windows\FRNFocusZone\FocusZoneComponentView.cpp:175-224`). +(`packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:175-224`). Reuse this request-versus-observation pattern. Do not report success merely because JavaScript called an optional `.focus()` method. @@ -94,7 +94,7 @@ This native implementation detail is not a promised public JavaScript API. consume every key. Sources: `packages\utils\interactive-hooks\src\useKeyProps.ts:8-95,114-126`; -`packages\components\FocusZone\windows\FRNFocusZone\FocusZoneComponentView.cpp:71-133`. +`packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:71-133`. ## Native and custom focus visuals diff --git a/.gitignore b/.gitignore index c89077c9ea..d0ff6b78de 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,12 @@ apps/*/.vscode/.react/ # Visual Studio .vs/ +# Native library outputs, including caches left behind by workspace relocations +packages/**/windows/**/[Oo]bj/ +packages/**/windows/**/Generated Files/ +packages/**/windows/**/x64/[Dd]ebug*/ +packages/**/windows/**/x64/[Rr]elease*/ + # Ignore Gradle build output directory **/android/**/build diff --git a/.oxfmtrc.json b/.oxfmtrc.json index 78d5f13b9a..4750ea7dac 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -10,7 +10,7 @@ "**/*.generated.*", "**/lib-commonjs/**", "**/dist/**", - "packages/native/Callout/windows/Callout/codegen/**", + "packages/agentic/native-core/windows/NativeCore/codegen/**", "**/CHANGELOG.*", "**/CODE_OF_CONDUCT.md", "**/SECURITY.md", diff --git a/AGENTS.md b/AGENTS.md index b2d3381a1c..f05cbd3606 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -88,7 +88,7 @@ This is the **FluentUI React Native** repository, a monorepo containing React Na /packages/ - Core library packages /agentic/native-core/ - Shared modern native components and TurboModules /components/ - UI component implementations (Button, Checkbox, Avatar, etc.) - /native/ - Standalone native-backed component packages (Callout, etc.) + /shim/ - Compatibility entrypoints (Callout, FocusZone, theming, etc.) /framework/ - Core theming and composition framework /composition/ - Component composition factory (current approach) /theme/ - Theme system @@ -238,9 +238,9 @@ The composition framework uses precise types for better type safety: ### Component Development -**Component Location**: Stable JavaScript components are in `/packages/components/`, standalone native-backed components are in -`/packages/native/`, and components under development are in `/packages/experimental/`. New modern native components and -modules share `/packages/agentic/native-core/`; follow its [organization plan](packages/agentic/native-core/README.md). +**Component Location**: Stable JavaScript components are in `/packages/components/`, components under development are in +`/packages/experimental/`, and compatibility packages are in `/packages/shim/`. Modern native components and modules share +`/packages/agentic/native-core/`; follow its [organization plan](packages/agentic/native-core/README.md). **Component Structure**: Each component typically has: @@ -274,24 +274,24 @@ modules share `/packages/agentic/native-core/`; follow its [organization plan](p - **Do not use barrel exports (`export * from '...'`)** - wildcard re-exports break tree-shaking because bundlers cannot statically determine which symbols are used, so unused code is retained in consumers' bundles. Always use explicit named re-exports instead, e.g. `export { Foo, Bar } from './module'` and `export type { Baz } from './module'`. -**Native Modules**: Standalone native-backed component packages, such as Callout, live in `/packages/native/`. Components with -native code (iOS/Android/Windows): +**Native Modules**: Callout and FocusZone implementations live in `@fluentui-react-native/native-core`; their former packages +are JS-only compatibility shims under `/packages/shim/`. Components with native code: - Typically have one root slot wrapping the native component - Use `codegenNativeComponent` for new architecture compatibility - May use `constantsToExport` for default values from native side - iOS/macOS: Include `.podspec` files -- Must be added to FluentTester's Podfile (transitive dependencies aren't autolinked) +- Native applications must directly depend on native-core for native project discovery and autolinking For new modern native components and modules, use `@fluentui-react-native/native-core` rather than creating another standalone package. Its Windows components are Fabric-only; macOS components support Paper and Fabric initially. Export shared APIs from the root and scope platform APIs to `macos`, `windows` (React Native Windows), `win` (Windows/Win32), or `win32`. -Existing native packages are not migrated by this convention. +Callout and FocusZone retain their existing public APIs and native registration names after migration. ### Creating a New Component -1. Create the package under `/packages/components/`, `/packages/native/` for a standalone - native-backed component, or `/packages/experimental/` while it is under development +1. Create the package under `/packages/components/` or `/packages/experimental/` while it is + under development. Add modern native components/modules to the existing `/packages/agentic/native-core/` package instead. 2. Copy structure from existing component (e.g., Shimmer, Button) 3. Update `package.json` with correct name and dependencies (use `workspace:*` for internal packages) 4. Add the new package's `tsconfig.json` to the root `tsconfig.json` `references` so it joins the unified build diff --git a/apps/E2E/tsconfig.json b/apps/E2E/tsconfig.json index 97bea26d4a..55864b6fde 100644 --- a/apps/E2E/tsconfig.json +++ b/apps/E2E/tsconfig.json @@ -10,7 +10,7 @@ "include": ["src"], "references": [ { - "path": "../../packages/components/FocusZone/tsconfig.json" + "path": "../../packages/shim/focus-zone/tsconfig.json" }, { "path": "../../scripts/tsconfig.json" diff --git a/apps/fluent-tester/package.json b/apps/fluent-tester/package.json index 71f307224c..dad808e8dc 100644 --- a/apps/fluent-tester/package.json +++ b/apps/fluent-tester/package.json @@ -38,6 +38,7 @@ "windows": "react-native run-windows --arch x64 --sln windows/FluentTester.sln" }, "dependencies": { + "@fluentui-react-native/native-core": "workspace:*", "@types/node": "catalog:", "@types/react": "~19.1.4", "react": "19.1.4", diff --git a/apps/fluent-tester/tsconfig.json b/apps/fluent-tester/tsconfig.json index 23bace6b87..ae103d0427 100644 --- a/apps/fluent-tester/tsconfig.json +++ b/apps/fluent-tester/tsconfig.json @@ -11,7 +11,7 @@ "include": ["src"], "references": [ { - "path": "../../packages/native/Callout/tsconfig.json" + "path": "../../packages/shim/callout/tsconfig.json" }, { "path": "../../packages/experimental/AppearanceAdditions/tsconfig.json" @@ -32,11 +32,14 @@ "path": "../../packages/experimental/Shimmer/tsconfig.json" }, { - "path": "../../packages/components/FocusZone/tsconfig.json" + "path": "../../packages/shim/focus-zone/tsconfig.json" }, { "path": "../../packages/components/MenuButton/tsconfig.json" }, + { + "path": "../../packages/agentic/native-core/tsconfig.json" + }, { "path": "../../packages/components/RadioGroup/tsconfig.json" }, diff --git a/apps/storybook/README.md b/apps/storybook/README.md index b24ae09060..ee18b76a50 100644 --- a/apps/storybook/README.md +++ b/apps/storybook/README.md @@ -1,10 +1,10 @@ # Agentic Components Storybook On-device [Storybook](https://storybook.js.org/) test app (Storybook for React Native v10) for -`@fluentui-react-native/components` and linked standalone native packages. It loads every -`*.stories.(ts|tsx)` file from the agentic components package plus the standalone Callout -package. FocusZone stories are agentic primitive stories, while its standalone native package -remains linked so they run in the Fabric and Paper hosts. +`@fluentui-react-native/components` and `@fluentui-react-native/native-core`. It loads every +`*.stories.(ts|tsx)` file from the agentic components package plus native-core's Callout +stories. FocusZone stories remain agentic primitive stories and use native-core's implementation. +Native-core owns the shared macOS pod and Windows Fabric library for both components. The reusable desktop CLI and configuration live in `packages/agentic/storybook-desktop`, with peer-dependent React Native @@ -129,9 +129,9 @@ by smoke so its bundle identity does not fall back to the generated app default. ## Running on Windows The Windows app also uses `react-native-test-app`. Its generated Win32 project uses React Native -Windows 0.81's New Architecture and Fabric renderer. The Callout and FocusZone packages are -autolinked as Windows Fabric native libraries; their Paper implementations remain available on -their supported legacy endpoints. +Windows 0.81's New Architecture and Fabric renderer. Native-core is autolinked as one Windows +Fabric native library that registers Callout and FocusZone. macOS retains both Paper and Fabric +adapters; Win32 continues using its host-provided native implementations. ```powershell # from this directory diff --git a/apps/storybook/agent-map.yaml b/apps/storybook/agent-map.yaml index df305c7a80..8039f0170e 100644 --- a/apps/storybook/agent-map.yaml +++ b/apps/storybook/agent-map.yaml @@ -15,7 +15,7 @@ sources: policy: generated-do-not-edit story_packages: - '@fluentui-react-native/components' - - '@fluentui-react-native/callout' + - '@fluentui-react-native/native-core' services: supervisor: diff --git a/apps/storybook/package.json b/apps/storybook/package.json index 4903ff03a7..13f134e41c 100644 --- a/apps/storybook/package.json +++ b/apps/storybook/package.json @@ -20,9 +20,8 @@ "format": "fluentui-scripts format" }, "dependencies": { - "@fluentui-react-native/callout": "workspace:*", "@fluentui-react-native/components": "workspace:*", - "@fluentui-react-native/focus-zone": "workspace:*", + "@fluentui-react-native/native-core": "workspace:*", "@fluentui-react-native/storybook-desktop": "workspace:*", "@fluentui-react-native/storybook-desktop-runtime": "workspace:*", "@office-iss/react-native-win32": "^0.81.0", @@ -57,9 +56,8 @@ "furn": { "knip": { "ignoreDependencies": [ - "@fluentui-react-native/callout", "@fluentui-react-native/components", - "@fluentui-react-native/focus-zone", + "@fluentui-react-native/native-core", "react-native-svg" ] } diff --git a/apps/storybook/storybook.config.mts b/apps/storybook/storybook.config.mts index a2c8541084..6284425899 100644 --- a/apps/storybook/storybook.config.mts +++ b/apps/storybook/storybook.config.mts @@ -29,7 +29,7 @@ export default makeDesktopStorybookConfig({ }, ], [ - '@fluentui-react-native/callout', + '@fluentui-react-native/native-core', { platformSettings: { windows: { diff --git a/apps/storybook/tsconfig.json b/apps/storybook/tsconfig.json index 2e379f55aa..3ce850c19e 100644 --- a/apps/storybook/tsconfig.json +++ b/apps/storybook/tsconfig.json @@ -16,16 +16,13 @@ "exclude": ["node_modules", "dist", ".cache"], "references": [ { - "path": "../../packages/agentic/storybook-desktop/tsconfig.json" - }, - { - "path": "../../packages/native/Callout/tsconfig.json" + "path": "../../packages/agentic/components/tsconfig.json" }, { - "path": "../../packages/agentic/components/tsconfig.json" + "path": "../../packages/agentic/desktop-driver/tsconfig.json" }, { - "path": "../../packages/components/FocusZone/tsconfig.json" + "path": "../../packages/agentic/native-core/tsconfig.json" }, { "path": "../../scripts/tsconfig.json" @@ -34,7 +31,7 @@ "path": "../../packages/agentic/storybook-desktop-runtime/tsconfig.json" }, { - "path": "../../packages/agentic/desktop-driver/tsconfig.json" + "path": "../../packages/agentic/storybook-desktop/tsconfig.json" } ] } diff --git a/apps/tester-core/tsconfig.json b/apps/tester-core/tsconfig.json index 7757310289..ee6c375fcf 100644 --- a/apps/tester-core/tsconfig.json +++ b/apps/tester-core/tsconfig.json @@ -29,7 +29,7 @@ "path": "../../packages/components/Button/tsconfig.json" }, { - "path": "../../packages/native/Callout/tsconfig.json" + "path": "../../packages/shim/callout/tsconfig.json" }, { "path": "../../packages/components/Chip/tsconfig.json" @@ -83,7 +83,7 @@ "path": "../../packages/experimental/Shimmer/tsconfig.json" }, { - "path": "../../packages/components/FocusZone/tsconfig.json" + "path": "../../packages/shim/focus-zone/tsconfig.json" }, { "path": "../../packages/framework-base/tsconfig.json" diff --git a/packages/agentic/components/WIN32-FOCUS-PLAN.md b/packages/agentic/components/WIN32-FOCUS-PLAN.md index ce765d75f6..c857fe0509 100644 --- a/packages/agentic/components/WIN32-FOCUS-PLAN.md +++ b/packages/agentic/components/WIN32-FOCUS-PLAN.md @@ -246,7 +246,7 @@ paths. The installed Win32 JavaScript inspected was `packages\components\Menu\src\MenuPopover\useMenuPopover.ts:42-84,100-118`. - **V6:** `packages\components\TabList\src\Tab\useTab.win32.ts:64-110,142-152`; `packages\components\TabList\src\TabList\useTabList.ts:142-176,183-200`. -- **V7:** `packages\components\FocusZone\src\FocusZone.tsx:15-45`; +- **V7:** `packages\agentic\native-core\src\components\focus-zone\FocusZone.tsx:15-45`; `packages\components\FocusTrapZone\src\FocusTrapZone.ts:15-31`. These wrappers do not by themselves prove native trap/restoration semantics. - **V8:** `apps\tester-core\src\TestComponents\FocusZone\FocusZoneE2ETest.tsx:1-120`; @@ -297,7 +297,7 @@ paths. The installed Win32 JavaScript inspected was ### Added Fabric, macOS, and modern-ref evidence -- **W1:** `packages\components\FocusZone\windows\FRNFocusZone\FocusZoneComponentView.cpp:33-65,71-133,175-224` +- **W1:** `packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:33-65,71-133,175-224` subscribes to native focus/key events, requests `TryFocus(FocusState::Keyboard)`, and checks actual focused identity. This is a Fabric implementation model for V1 outcomes, not a reason to copy its transport to Win32. @@ -316,18 +316,18 @@ paths. The installed Win32 JavaScript inspected was implements AppKit focus/blur, ring-mask drawing, responder events, and pointer focus behavior. Lines 1807-1861 emit key events and use native handled-key entries to decide whether AppKit receives `super` handling. -- **M3:** `packages\components\FocusZone\macos\RCTFocusZone.m:24-35,122-129,584-675` +- **M3:** `packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:24-35,122-129,584-675` resolves editable focus targets and key-view navigation, including a hierarchy fallback for Fabric-backed zones. - `packages\components\FocusZone\macos\RCTFocusZoneComponentView.mm:118-195` + `packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-195` forwards focus to its content rather than becoming a duplicate tab stop and clears default-responder state on recycle. -- **M4:** `packages\native\Callout\macos\CalloutWindow.swift:9-38`; - `packages\native\Callout\macos\CalloutView.swift:33-45,142-216`; - `packages\native\Callout\macos\RCTCalloutComponentView.mm:96-132`. +- **M4:** `packages\agentic\native-core\macos\components\Callout\shared\CalloutWindow.swift:9-38`; + `packages\agentic\native-core\macos\components\Callout\shared\CalloutView.swift:33-45,142-216`; + `packages\agentic\native-core\macos\components\Callout\fabric\RCTCalloutComponentView.mm:96-132`. Popup key-window activation, initial child focus, and a separate Fabric touch attachment are distinct concerns. - `packages\native\Callout\macos\GuardedEventMonitor.swift:1-29` owns monitor cleanup. + `packages\agentic\native-core\macos\components\Callout\shared\GuardedEventMonitor.swift:1-29` owns monitor cleanup. - **M5:** `packages\components\ContextualMenu\src\ContextualMenu.tsx:39-55` contains a macOS layout-effect/timer workaround: a readiness problem to solve, not a general timer recipe. @@ -341,7 +341,7 @@ paths. The installed Win32 JavaScript inspected was covers stable native refs and compatible replacements. - **R2:** `packages\agentic\components\src\refs.types.test.tsx:30-84` distinguishes structural and pressable root refs. - `packages\native\Callout\src\CalloutNativeComponent.ts:63-70` demonstrates + `packages\agentic\native-core\src\specs\components\CalloutNativeComponent.ts:63-70` demonstrates typed native commands with host refs rather than global tag arguments. ### macOS adaptation decisions diff --git a/packages/agentic/components/package.json b/packages/agentic/components/package.json index 5a5783aa18..846c5c4527 100644 --- a/packages/agentic/components/package.json +++ b/packages/agentic/components/package.json @@ -40,14 +40,13 @@ "update-snapshots": "fluentui-scripts jest -u" }, "dependencies": { - "@fluentui-react-native/callout": "workspace:*", "@fluentui-react-native/design": "workspace:*", - "@fluentui-react-native/framework-base": "workspace:*" + "@fluentui-react-native/framework-base": "workspace:*", + "@fluentui-react-native/native-core": "workspace:*" }, "devDependencies": { "@babel/core": "catalog:", "@fluentui-react-native/desktop-driver": "workspace:*", - "@fluentui-react-native/focus-zone": "workspace:*", "@fluentui-react-native/scripts": "workspace:*", "@fluentui-react-native/storybook-desktop": "workspace:*", "@office-iss/react-native-win32": "^0.81.0", @@ -94,7 +93,7 @@ "jestPlatform": "macos", "knip": { "ignoreDependencies": [ - "@fluentui-react-native/callout" + "@fluentui-react-native/native-core" ] } }, diff --git a/packages/agentic/components/src/primitives/focus-zone/focus-zone.stories.tsx b/packages/agentic/components/src/primitives/focus-zone/focus-zone.stories.tsx index bb86787ff3..2463df8fe9 100644 --- a/packages/agentic/components/src/primitives/focus-zone/focus-zone.stories.tsx +++ b/packages/agentic/components/src/primitives/focus-zone/focus-zone.stories.tsx @@ -4,8 +4,8 @@ import { Platform, Pressable, StyleSheet, Text, View } from 'react-native'; import type { StyleProp, ViewStyle } from 'react-native'; import type { WdioStory } from '@fluentui-react-native/storybook-desktop/testing'; -import { FocusZone } from '@fluentui-react-native/focus-zone'; -import type { FocusZoneDirection, FocusZoneProps, FocusZoneTabNavigation } from '@fluentui-react-native/focus-zone'; +import { FocusZone } from '@fluentui-react-native/native-core'; +import type { FocusZoneDirection, FocusZoneProps, FocusZoneTabNavigation } from '@fluentui-react-native/native-core'; import type { Meta, StoryObj } from '@storybook/react-native'; import { Button } from '../../components/button/button'; diff --git a/packages/agentic/components/tsconfig.json b/packages/agentic/components/tsconfig.json index 1b9942a25a..965178a7ed 100644 --- a/packages/agentic/components/tsconfig.json +++ b/packages/agentic/components/tsconfig.json @@ -9,9 +9,6 @@ "include": ["src"], "exclude": ["**/*.stories.ts", "**/*.stories.tsx", "**/*.story-helpers.tsx", "**/*.wdio.ts"], "references": [ - { - "path": "../../native/Callout/tsconfig.json" - }, { "path": "../design/tsconfig.json" }, @@ -19,16 +16,16 @@ "path": "../desktop-driver/tsconfig.json" }, { - "path": "../storybook-desktop/tsconfig.json" + "path": "../../framework-base/tsconfig.json" }, { - "path": "../../components/FocusZone/tsconfig.json" + "path": "../native-core/tsconfig.json" }, { - "path": "../../framework-base/tsconfig.json" + "path": "../../../scripts/tsconfig.json" }, { - "path": "../../../scripts/tsconfig.json" + "path": "../storybook-desktop/tsconfig.json" } ] } diff --git a/packages/native/Callout/FRNCallout.podspec b/packages/agentic/native-core/FRNNativeCore.podspec similarity index 82% rename from packages/native/Callout/FRNCallout.podspec rename to packages/agentic/native-core/FRNNativeCore.podspec index 5a46aedb2a..3165e1234c 100644 --- a/packages/native/Callout/FRNCallout.podspec +++ b/packages/agentic/native-core/FRNNativeCore.podspec @@ -3,8 +3,8 @@ require 'json' package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) Pod::Spec.new do |s| - s.name = 'FRNCallout' - s.module_name = 'FRNCallout' + s.name = 'FRNNativeCore' + s.module_name = 'FRNNativeCore' s.version = package['version'] s.summary = package['description'] s.license = package['license'] @@ -16,7 +16,7 @@ Pod::Spec.new do |s| s.swift_version = "5" s.osx.deployment_target = "10.15" - s.osx.source_files = "macos/*.{swift,h,m,mm}" + s.osx.source_files = "macos/**/*.{swift,h,m,mm}" s.dependency 'React' diff --git a/packages/native/Callout/NuGet.config b/packages/agentic/native-core/NuGet.config similarity index 100% rename from packages/native/Callout/NuGet.config rename to packages/agentic/native-core/NuGet.config diff --git a/packages/agentic/native-core/README.md b/packages/agentic/native-core/README.md index 5816756caf..d1dba69b34 100644 --- a/packages/agentic/native-core/README.md +++ b/packages/agentic/native-core/README.md @@ -7,18 +7,25 @@ Higher-order Fluent components, theming, and composition remain in their existing packages and can consume this package without introducing a reverse dependency. -## Initialization status - -The workspace, TypeScript build, package export map, and entrypoint contract -tests are initialized. All five entrypoints are intentionally empty. There are -no native implementations, codegen specs, native projects, or autolinking -registrations yet. This does not migrate Callout, FocusZone, or any other -existing package. - -The initial shell has no runtime dependencies. Add React Native/React peers, -optional platform-fork peers, development dependencies, and matching `rnx-kit` -capabilities with the first implementation, using the repository's supported -versions. Do not imply native compatibility from an empty entrypoint. +## Current contents + +The root exports the existing unstyled `Callout` and `FocusZone` wrappers and +their public types. Their JS, tests, specifications, macOS implementations, +and Windows Fabric implementations live here. The `macos`, `windows`, `win`, +and `win32` entrypoints are reserved for future host-specific APIs and remain +empty; shared components belong to the root. + +`@fluentui-react-native/callout` and `@fluentui-react-native/focus-zone` are +JS-only compatibility shims under `packages/shim`. Deprecated type aliases and +native registration names are preserved. The root uses the component-qualified +`FocusZoneNativeProps`; the FocusZone shim retains its old `NativeProps` name. + +Native applications must directly depend on this package for autolinking. +macOS now uses one `FRNNativeCore` pod, and Windows uses one +`FRNNativeCore.ReactPackageProvider` library for both components. Remove +explicit old `FRNCallout`/`RCTFocusZone` pod or standalone Windows project +references and regenerate the consuming native project/Pods. Win32 continues +to use its host-provided `RCTCallout` and `RCTFocusZone`. ## Public entrypoints @@ -50,8 +57,9 @@ imports. Never use wildcard re-exports. ## JavaScript and specification layout -Only the entrypoint files exist initially. Add the following directories as -real features arrive rather than filling them with placeholder native APIs: +Component wrappers live in `src/components/callout` and +`src/components/focus-zone`, with codegen inputs in `src/specs/components`. +Add module and internal utility directories as real features arrive: ```text src/ @@ -148,15 +156,16 @@ and expose native failures. ## Codegen, registration, and publishing plan -Introduce native build metadata with the first real schema and native target: +The shared native build metadata follows this organization: 1. Use one package-level codegen identity, `FRNNativeCoreSpec`, with `type: "all"` and `jsSrcsDir: "src/specs"` so components and modules remain distinct in source but share a generation boundary. 2. Name component schemas `NativeComponent.ts` and module schemas `Native.ts`. Prefix native registration names with `FRNNativeCore` - to avoid collisions with existing packages; keep each name identical in - the schema, generated interface, native registration, and JS binding. + for new APIs to avoid collisions. Migrated Callout and FocusZone retain + their existing Fabric and Paper names for compatibility. Keep names + identical in the schema, generated interface, registration, and JS binding. 3. Configure Windows with both `componentsWindows` and `modulesWindows` generators and `windows/NativeCore/codegen` output. Apple codegen should run through the consuming macOS app's Pod integration. Inspect each @@ -165,12 +174,13 @@ Introduce native build metadata with the first real schema and native target: 4. Leave `includesGeneratedCode` unset unless generated artifacts for every supported native platform are deliberately shipped and verified. It is not a Windows-only switch and can prevent Apple code generation. -5. Add the macOS podspec, Windows project/provider, and autolinking metadata - only when they refer to real sources. Exclude Paper adapters from macOS - Fabric compilation and Fabric adapters from Paper compilation. Register - only implementations supported by the active host/architecture. +5. The macOS podspec includes both components' shared and Paper sources. + Fabric component views retain their `RCT_NEW_ARCH_ENABLED` guards. Paper + managers remain available for existing bridge interoperability until the + supported macOS hosts are Fabric-only. Windows declares `RnwNewArchOnly` + and registers both components through one package provider. 6. Keep native targets and required generated outputs in the published - archive. The initial `files` allowlist reserves `src`, `lib`, `macos`, + archive. The `files` allowlist includes `src`, `lib`, `macos`, `windows`, `win32`, podspecs, and `react-native.config.cjs`. Intermediate native build artifacts, tests, and machine-local project output must not leak into it. @@ -181,8 +191,9 @@ Introduce native build metadata with the first real schema and native target: ## Delivery and validation Add each feature as one reviewed contract, thin wrapper/schema, native -implementation, explicit export, and tests. Start with one representative -Fabric component and one TurboModule to exercise both codegen paths. +implementation, explicit export, and tests. The component codegen path is +exercised by Callout and FocusZone; add a representative TurboModule before +claiming module readiness. macOS component admission requires both Paper and Fabric evidence; Windows component admission requires Fabric evidence. A `win` or root API also requires Win32 evidence before it is exported there. @@ -194,12 +205,15 @@ yarn workspace @fluentui-react-native/native-core format --check yarn workspace @fluentui-react-native/native-core lint yarn workspace @fluentui-react-native/native-core build yarn workspace @fluentui-react-native/native-core test +yarn workspace @fluentui-react-native/native-core codegen:windows --check yarn build ``` -The initial Node tests validate all five export maps, emitted JS/declaration -files, and conditional resolution for compiled, React Native source, and -TypeScript entrypoints. Add the repository's shared Jest configuration and -native integration tests when wrappers and native targets exist. Native +Node tests validate the five export maps, emitted JS/declaration files, +conditional resolution, shared native ownership, and JS-only shims. Colocated +Jest tests retain wrapper behavior, imperative refs/commands, slot acceptance, +and public type contracts; shim tests verify exact component identity. +The package prebuild regenerates Windows bindings from the shared spec. +Native verification must cover registration, events/commands, refs, unmount cleanup, error propagation, and architecture/platform parity, not only JS mocks. diff --git a/packages/components/FocusZone/jest.config.cjs b/packages/agentic/native-core/jest.config.cjs similarity index 100% rename from packages/components/FocusZone/jest.config.cjs rename to packages/agentic/native-core/jest.config.cjs diff --git a/packages/native/Callout/macos/RCTCalloutComponentView.h b/packages/agentic/native-core/macos/components/Callout/fabric/RCTCalloutComponentView.h similarity index 100% rename from packages/native/Callout/macos/RCTCalloutComponentView.h rename to packages/agentic/native-core/macos/components/Callout/fabric/RCTCalloutComponentView.h diff --git a/packages/native/Callout/macos/RCTCalloutComponentView.mm b/packages/agentic/native-core/macos/components/Callout/fabric/RCTCalloutComponentView.mm similarity index 95% rename from packages/native/Callout/macos/RCTCalloutComponentView.mm rename to packages/agentic/native-core/macos/components/Callout/fabric/RCTCalloutComponentView.mm index 86a6d5a259..2dee187651 100644 --- a/packages/native/Callout/macos/RCTCalloutComponentView.mm +++ b/packages/agentic/native-core/macos/components/Callout/fabric/RCTCalloutComponentView.mm @@ -2,10 +2,10 @@ #import "RCTCalloutComponentView.h" -#import -#import -#import -#import +#import +#import +#import +#import #import #import @@ -13,7 +13,7 @@ #import #import -#import "FRNCallout-Swift.h" +#import "FRNNativeCore-Swift.h" using namespace facebook::react; diff --git a/packages/native/Callout/macos/CalloutManager.swift b/packages/agentic/native-core/macos/components/Callout/paper/CalloutManager.swift similarity index 100% rename from packages/native/Callout/macos/CalloutManager.swift rename to packages/agentic/native-core/macos/components/Callout/paper/CalloutManager.swift diff --git a/packages/native/Callout/macos/FRNCalloutManager.h b/packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.h similarity index 100% rename from packages/native/Callout/macos/FRNCalloutManager.h rename to packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.h diff --git a/packages/native/Callout/macos/FRNCalloutManager.m b/packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.m similarity index 98% rename from packages/native/Callout/macos/FRNCalloutManager.m rename to packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.m index ca5ebd4b58..f162f6cc3b 100644 --- a/packages/native/Callout/macos/FRNCalloutManager.m +++ b/packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.m @@ -1,6 +1,6 @@ #import "FRNCalloutManager.h" -#import "FRNCallout-Swift.h" +#import "FRNNativeCore-Swift.h" @implementation RCTConvert (FRNCalloutAdditions) diff --git a/packages/native/Callout/macos/CalloutView.swift b/packages/agentic/native-core/macos/components/Callout/shared/CalloutView.swift similarity index 100% rename from packages/native/Callout/macos/CalloutView.swift rename to packages/agentic/native-core/macos/components/Callout/shared/CalloutView.swift diff --git a/packages/native/Callout/macos/CalloutWindow.swift b/packages/agentic/native-core/macos/components/Callout/shared/CalloutWindow.swift similarity index 100% rename from packages/native/Callout/macos/CalloutWindow.swift rename to packages/agentic/native-core/macos/components/Callout/shared/CalloutWindow.swift diff --git a/packages/native/Callout/macos/FRNCallout.h b/packages/agentic/native-core/macos/components/Callout/shared/FRNCallout.h similarity index 57% rename from packages/native/Callout/macos/FRNCallout.h rename to packages/agentic/native-core/macos/components/Callout/shared/FRNCallout.h index a938f891a5..86c796ed89 100644 --- a/packages/native/Callout/macos/FRNCallout.h +++ b/packages/agentic/native-core/macos/components/Callout/shared/FRNCallout.h @@ -4,7 +4,7 @@ // // Created by Patrick Boyd on 9/24/24. // -// Created as a dummy file to enable importing FRNCallout-Swift.h and invoking Swift functions from Obj-C +// Created as a dummy file to enable importing FRNNativeCore-Swift.h and invoking Swift functions from Obj-C // #ifndef FRNCallout_h diff --git a/packages/native/Callout/macos/FlippedVisualEffectView.swift b/packages/agentic/native-core/macos/components/Callout/shared/FlippedVisualEffectView.swift similarity index 100% rename from packages/native/Callout/macos/FlippedVisualEffectView.swift rename to packages/agentic/native-core/macos/components/Callout/shared/FlippedVisualEffectView.swift diff --git a/packages/native/Callout/macos/GuardedEventMonitor.swift b/packages/agentic/native-core/macos/components/Callout/shared/GuardedEventMonitor.swift similarity index 100% rename from packages/native/Callout/macos/GuardedEventMonitor.swift rename to packages/agentic/native-core/macos/components/Callout/shared/GuardedEventMonitor.swift diff --git a/packages/components/FocusZone/macos/RCTFocusZoneComponentView.h b/packages/agentic/native-core/macos/components/FocusZone/fabric/RCTFocusZoneComponentView.h similarity index 100% rename from packages/components/FocusZone/macos/RCTFocusZoneComponentView.h rename to packages/agentic/native-core/macos/components/FocusZone/fabric/RCTFocusZoneComponentView.h diff --git a/packages/components/FocusZone/macos/RCTFocusZoneComponentView.mm b/packages/agentic/native-core/macos/components/FocusZone/fabric/RCTFocusZoneComponentView.mm similarity index 94% rename from packages/components/FocusZone/macos/RCTFocusZoneComponentView.mm rename to packages/agentic/native-core/macos/components/FocusZone/fabric/RCTFocusZoneComponentView.mm index 2905eb6fbb..8326d4d086 100644 --- a/packages/components/FocusZone/macos/RCTFocusZoneComponentView.mm +++ b/packages/agentic/native-core/macos/components/FocusZone/fabric/RCTFocusZoneComponentView.mm @@ -2,14 +2,14 @@ #import "RCTFocusZoneComponentView.h" -#import -#import -#import -#import +#import +#import +#import +#import #import -#import "RCTFocusZone.h" +#import "../shared/RCTFocusZone.h" using namespace facebook::react; diff --git a/packages/components/FocusZone/macos/RCTFocusZoneManager.h b/packages/agentic/native-core/macos/components/FocusZone/paper/RCTFocusZoneManager.h similarity index 100% rename from packages/components/FocusZone/macos/RCTFocusZoneManager.h rename to packages/agentic/native-core/macos/components/FocusZone/paper/RCTFocusZoneManager.h diff --git a/packages/components/FocusZone/macos/RCTFocusZoneManager.m b/packages/agentic/native-core/macos/components/FocusZone/paper/RCTFocusZoneManager.m similarity index 97% rename from packages/components/FocusZone/macos/RCTFocusZoneManager.m rename to packages/agentic/native-core/macos/components/FocusZone/paper/RCTFocusZoneManager.m index 68b93f136b..7d4eddaa48 100644 --- a/packages/components/FocusZone/macos/RCTFocusZoneManager.m +++ b/packages/agentic/native-core/macos/components/FocusZone/paper/RCTFocusZoneManager.m @@ -1,4 +1,4 @@ -#import "RCTFocusZone.h" +#import "../shared/RCTFocusZone.h" #import "RCTFocusZoneManager.h" #import #import diff --git a/packages/components/FocusZone/macos/KeyCodes.h b/packages/agentic/native-core/macos/components/FocusZone/shared/KeyCodes.h similarity index 100% rename from packages/components/FocusZone/macos/KeyCodes.h rename to packages/agentic/native-core/macos/components/FocusZone/shared/KeyCodes.h diff --git a/packages/components/FocusZone/macos/RCTFocusZone.h b/packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.h similarity index 100% rename from packages/components/FocusZone/macos/RCTFocusZone.h rename to packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.h diff --git a/packages/components/FocusZone/macos/RCTFocusZone.m b/packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.m similarity index 100% rename from packages/components/FocusZone/macos/RCTFocusZone.m rename to packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.m diff --git a/packages/agentic/native-core/package.json b/packages/agentic/native-core/package.json index 6542e731eb..408487ba1f 100644 --- a/packages/agentic/native-core/package.json +++ b/packages/agentic/native-core/package.json @@ -15,8 +15,19 @@ "macos", "windows", "win32", + "NuGet.config", "*.podspec", - "react-native.config.cjs" + "react-native.config.cjs", + "!src/**/__tests__/**", + "!src/**/*.test.*", + "!lib/**/__tests__/**", + "!lib/**/*.test.*", + "!windows/**/obj/**", + "!windows/**/x64/**", + "!windows/**/ARM64/**", + "!windows/**/Win32/**", + "!windows/**/Generated Files/**", + "!windows/**/packages/**" ], "type": "module", "sideEffects": false, @@ -59,15 +70,105 @@ "scripts": { "build": "tsc -b", "clean": "fluentui-scripts clean", + "codegen:windows": "react-native codegen-windows --no-telemetry", "format": "fluentui-scripts format", "lint": "fluentui-scripts lint", - "test": "yarn build && node --test tests/entrypoints.test.mjs" + "prebuild": "yarn codegen:windows", + "test": "yarn build && node --test tests/entrypoints.test.mjs tests/native-ownership.test.mjs && fluentui-scripts jest", + "update-snapshots": "fluentui-scripts jest -u" + }, + "dependencies": { + "@fluentui-react-native/framework-base": "workspace:*", + "@fluentui-react-native/interactive-hooks": "workspace:*" }, "devDependencies": { - "@fluentui-react-native/scripts": "workspace:*" + "@babel/core": "catalog:", + "@fluentui-react-native/scripts": "workspace:*", + "@office-iss/react-native-win32": "^0.81.8", + "@react-native-community/cli": "^20.0.0", + "@react-native-community/cli-platform-android": "^20.0.0", + "@react-native-community/cli-platform-ios": "^20.0.0", + "@react-native-windows/cli": "^0.81.0", + "@react-native/babel-preset": "^0.81.0", + "@react-native/metro-config": "^0.81.0", + "@storybook/react-native": "^10.4.7", + "@testing-library/react-native": "catalog:", + "@types/react": "~19.1.4", + "@types/react-test-renderer": "^19.1.0", + "react": "19.1.4", + "react-native": "^0.81.6", + "react-native-macos": "^0.81.0", + "react-native-windows": "^0.81.0", + "react-test-renderer": "19.1.4", + "storybook": "^10.4.0", + "test-renderer": "catalog:" + }, + "peerDependencies": { + "@office-iss/react-native-win32": "^0.74.0 || ^0.81.0", + "@types/react": "~18.2.0 || ~19.0.0 || ~19.1.4", + "react": "18.2.0 || 19.0.0 || 19.1.4", + "react-native": "^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.6", + "react-native-macos": "^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.0", + "react-native-windows": "^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.0" + }, + "peerDependenciesMeta": { + "@office-iss/react-native-win32": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "react-native-macos": { + "optional": true + }, + "react-native-windows": { + "optional": true + } + }, + "codegenConfig": { + "name": "FRNNativeCoreSpec", + "type": "all", + "jsSrcsDir": "src/specs", + "ios": { + "componentProvider": { + "Callout": "RCTCalloutComponentView", + "FocusZone": "RCTFocusZoneComponentView" + } + }, + "windows": { + "generators": [ + "componentsWindows", + "modulesWindows" + ], + "namespace": "FRNNativeCoreCodegen", + "outputDirectory": "windows/NativeCore/codegen", + "separateDataTypes": true + } + }, + "furn": { + "jestPlatform": "win32" + }, + "react-native-windows": { + "init-windows": { + "name": "FRNNativeCore", + "namespace": "FRNNativeCore", + "template": "cpp-lib" + } }, "rnx-kit": { "kitType": "library", + "alignDeps": { + "capabilities": [ + "babel-preset-react-native", + "core", + "core-android", + "core-ios", + "core-macos", + "core-windows", + "react", + "react-test-renderer" + ] + }, "extends": "@fluentui-react-native/scripts/kit-config" } } diff --git a/packages/native/Callout/react-native.config.cjs b/packages/agentic/native-core/react-native.config.cjs similarity index 78% rename from packages/native/Callout/react-native.config.cjs rename to packages/agentic/native-core/react-native.config.cjs index 5046885ab4..d495c666a0 100644 --- a/packages/native/Callout/react-native.config.cjs +++ b/packages/agentic/native-core/react-native.config.cjs @@ -5,7 +5,7 @@ module.exports = { sourceDir: 'windows', projects: [ { - projectFile: 'Callout/Callout.vcxproj', + projectFile: 'NativeCore/NativeCore.vcxproj', directDependency: true, }, ], diff --git a/packages/native/Callout/src/Callout.stories.tsx b/packages/agentic/native-core/src/components/callout/Callout.stories.tsx similarity index 100% rename from packages/native/Callout/src/Callout.stories.tsx rename to packages/agentic/native-core/src/components/callout/Callout.stories.tsx diff --git a/packages/native/Callout/src/Callout.tsx b/packages/agentic/native-core/src/components/callout/Callout.tsx similarity index 97% rename from packages/native/Callout/src/Callout.tsx rename to packages/agentic/native-core/src/components/callout/Callout.tsx index 6d0a9e33e3..b9fdd5da92 100644 --- a/packages/native/Callout/src/Callout.tsx +++ b/packages/agentic/native-core/src/components/callout/Callout.tsx @@ -6,7 +6,7 @@ import { directComponent, mergeProps, phasedComponent } from '@fluentui-react-na import type { CalloutProps } from './Callout.types'; import { calloutName } from './Callout.types'; -import NativeCalloutView, { Commands } from './CalloutNativeComponent'; +import NativeCalloutView, { Commands } from '../../specs/components/CalloutNativeComponent'; const colorTransparent = '#00000000'; diff --git a/packages/native/Callout/src/Callout.types.ts b/packages/agentic/native-core/src/components/callout/Callout.types.ts similarity index 100% rename from packages/native/Callout/src/Callout.types.ts rename to packages/agentic/native-core/src/components/callout/Callout.types.ts diff --git a/packages/native/Callout/SPEC.md b/packages/agentic/native-core/src/components/callout/SPEC.md similarity index 77% rename from packages/native/Callout/SPEC.md rename to packages/agentic/native-core/src/components/callout/SPEC.md index 92fd3b3497..e57ccfeac8 100644 --- a/packages/native/Callout/SPEC.md +++ b/packages/agentic/native-core/src/components/callout/SPEC.md @@ -4,8 +4,8 @@ Callout is an unstyled native primitive that presents children in transient UI positioned relative to a target ref, registered native anchor, or anchor -rectangle. It remains a standalone package because it owns native code and -CocoaPods integration. +rectangle. Its JS wrapper, native adapters, and codegen schema are owned by +`@fluentui-react-native/native-core`; the old Callout package is a JS-only shim. ## Contract @@ -27,8 +27,8 @@ CocoaPods integration. - macOS presents children in a native popup window and supports window focus commands through both Paper and Fabric component registrations. -- Windows uses the platform's built-in Paper `RCTCallout` and supplies a - package-owned Fabric registration for React Native Windows 0.81 and newer. +- Windows uses native-core's Fabric registration for React Native Windows + 0.81 and newer. Native-core supplies no Windows Paper implementation. - Win32 supplies the platform `RCTCallout` implementation and supports native dismissal, pointer capture, beak, and focus-restoration behavior. - Unsupported native behavior remains platform-defined rather than being @@ -39,6 +39,10 @@ CocoaPods integration. `ICalloutProps`, `ICalloutTokens`, and `CalloutNativeCommands` remain as deprecated aliases for the modern public types. +Existing `Callout`/`RCTCallout` native registration names remain unchanged. +Native applications must directly depend on `@fluentui-react-native/native-core` +so native tooling discovers the shared pod and Windows project. + ## Demonstration Interactive scenarios live in diff --git a/packages/native/Callout/src/__tests__/Callout.test.tsx b/packages/agentic/native-core/src/components/callout/__tests__/Callout.test.tsx similarity index 98% rename from packages/native/Callout/src/__tests__/Callout.test.tsx rename to packages/agentic/native-core/src/components/callout/__tests__/Callout.test.tsx index 959a94e4fc..bd10a9a60f 100644 --- a/packages/native/Callout/src/__tests__/Callout.test.tsx +++ b/packages/agentic/native-core/src/components/callout/__tests__/Callout.test.tsx @@ -8,7 +8,7 @@ import type { SlotProp } from '@fluentui-react-native/framework-base'; import { Callout } from '../Callout'; import type { CalloutHandle } from '../Callout.types'; -import { Commands } from '../CalloutNativeComponent'; +import { Commands } from '../../../specs/components/CalloutNativeComponent'; jest.mock('react-native/Libraries/ReactNative/RendererProxy', () => ({ ...jest.requireActual('react-native/Libraries/ReactNative/RendererProxy'), diff --git a/packages/native/Callout/src/__tests__/Callout.types.test.ts b/packages/agentic/native-core/src/components/callout/__tests__/Callout.types.test.ts similarity index 100% rename from packages/native/Callout/src/__tests__/Callout.types.test.ts rename to packages/agentic/native-core/src/components/callout/__tests__/Callout.types.test.ts diff --git a/packages/native/Callout/src/__tests__/__snapshots__/Callout.test.tsx.snap b/packages/agentic/native-core/src/components/callout/__tests__/__snapshots__/Callout.test.tsx.snap similarity index 100% rename from packages/native/Callout/src/__tests__/__snapshots__/Callout.test.tsx.snap rename to packages/agentic/native-core/src/components/callout/__tests__/__snapshots__/Callout.test.tsx.snap diff --git a/packages/components/FocusZone/src/FocusZone.tsx b/packages/agentic/native-core/src/components/focus-zone/FocusZone.tsx similarity index 96% rename from packages/components/FocusZone/src/FocusZone.tsx rename to packages/agentic/native-core/src/components/focus-zone/FocusZone.tsx index 7624ee2f2b..0430f6c112 100644 --- a/packages/components/FocusZone/src/FocusZone.tsx +++ b/packages/agentic/native-core/src/components/focus-zone/FocusZone.tsx @@ -7,7 +7,7 @@ import { useViewCommandFocus } from '@fluentui-react-native/interactive-hooks'; import type { FocusZoneProps } from './FocusZone.types'; import { focusZoneName } from './FocusZone.types'; -import NativeFocusZone from './FocusZoneNativeComponent'; +import NativeFocusZone from '../../specs/components/FocusZoneNativeComponent'; /** * Renders the native FocusZone without applying theme or appearance defaults. diff --git a/packages/components/FocusZone/src/FocusZone.types.ts b/packages/agentic/native-core/src/components/focus-zone/FocusZone.types.ts similarity index 100% rename from packages/components/FocusZone/src/FocusZone.types.ts rename to packages/agentic/native-core/src/components/focus-zone/FocusZone.types.ts diff --git a/packages/components/FocusZone/SPEC.md b/packages/agentic/native-core/src/components/focus-zone/SPEC.md similarity index 83% rename from packages/components/FocusZone/SPEC.md rename to packages/agentic/native-core/src/components/focus-zone/SPEC.md index 54ce2640a9..5379e8851c 100644 --- a/packages/components/FocusZone/SPEC.md +++ b/packages/agentic/native-core/src/components/focus-zone/SPEC.md @@ -3,8 +3,9 @@ ## Purpose FocusZone is an unstyled native primitive that coordinates directional and Tab -keyboard navigation among focusable descendants. It remains a standalone package -because it owns native macOS code and CocoaPods integration. +keyboard navigation among focusable descendants. Its JS wrapper, native +adapters, and codegen schema are owned by `@fluentui-react-native/native-core`; +the old FocusZone package is a JS-only shim. ## Contract @@ -45,6 +46,12 @@ because it owns native macOS code and CocoaPods integration. `FocusZoneRenderData`, and `FocusZoneType` remain as deprecated compatibility types. +Existing `FocusZone`/`RCTFocusZone` native registration names remain unchanged. +The old package's `NativeProps` export aliases native-core's +`FocusZoneNativeProps`. Native applications must directly depend on +`@fluentui-react-native/native-core` so native tooling discovers the shared pod +and Windows project. + ## Demonstration Interactive directional, circular, Tab, disabled, and default-focus scenarios diff --git a/packages/components/FocusZone/src/__tests__/FocusZone.test.tsx b/packages/agentic/native-core/src/components/focus-zone/__tests__/FocusZone.test.tsx similarity index 100% rename from packages/components/FocusZone/src/__tests__/FocusZone.test.tsx rename to packages/agentic/native-core/src/components/focus-zone/__tests__/FocusZone.test.tsx diff --git a/packages/components/FocusZone/src/__tests__/FocusZone.types.test.ts b/packages/agentic/native-core/src/components/focus-zone/__tests__/FocusZone.types.test.ts similarity index 100% rename from packages/components/FocusZone/src/__tests__/FocusZone.types.test.ts rename to packages/agentic/native-core/src/components/focus-zone/__tests__/FocusZone.types.test.ts diff --git a/packages/agentic/native-core/src/index.ts b/packages/agentic/native-core/src/index.ts index cb0ff5c3b5..a576fa7f8d 100644 --- a/packages/agentic/native-core/src/index.ts +++ b/packages/agentic/native-core/src/index.ts @@ -1 +1,27 @@ -export {}; +export { Callout } from './components/callout/Callout'; +export { calloutName } from './components/callout/Callout.types'; +export type { + CalloutHandle, + CalloutNativeCommands, + CalloutProps, + CalloutTokens, + DirectionalHint, + DismissBehaviors, + ICalloutProps, + ICalloutTokens, + RestoreFocusEvent, +} from './components/callout/Callout.types'; +export { FocusZone } from './components/focus-zone/FocusZone'; +export { focusZoneName } from './components/focus-zone/FocusZone.types'; +export type { + FocusZoneDirection, + FocusZoneProps, + FocusZoneRenderData, + FocusZoneSlotProps, + FocusZoneState, + FocusZoneTabNavigation, + FocusZoneTokens, + FocusZoneType, + NativeProps as FocusZoneNativeProps, + NavigateAtEnd, +} from './components/focus-zone/FocusZone.types'; diff --git a/packages/native/Callout/src/CalloutNativeComponent.ts b/packages/agentic/native-core/src/specs/components/CalloutNativeComponent.ts similarity index 100% rename from packages/native/Callout/src/CalloutNativeComponent.ts rename to packages/agentic/native-core/src/specs/components/CalloutNativeComponent.ts diff --git a/packages/components/FocusZone/src/FocusZoneNativeComponent.ts b/packages/agentic/native-core/src/specs/components/FocusZoneNativeComponent.ts similarity index 100% rename from packages/components/FocusZone/src/FocusZoneNativeComponent.ts rename to packages/agentic/native-core/src/specs/components/FocusZoneNativeComponent.ts diff --git a/packages/components/FocusZone/src/FocusZoneNativeComponent.win32.ts b/packages/agentic/native-core/src/specs/components/FocusZoneNativeComponent.win32.ts similarity index 100% rename from packages/components/FocusZone/src/FocusZoneNativeComponent.win32.ts rename to packages/agentic/native-core/src/specs/components/FocusZoneNativeComponent.win32.ts diff --git a/packages/agentic/native-core/tests/native-ownership.test.mjs b/packages/agentic/native-core/tests/native-ownership.test.mjs new file mode 100644 index 0000000000..bb24deee3d --- /dev/null +++ b/packages/agentic/native-core/tests/native-ownership.test.mjs @@ -0,0 +1,63 @@ +import assert from 'node:assert/strict'; +import { readFile, readdir } from 'node:fs/promises'; +import { createRequire } from 'node:module'; +import test from 'node:test'; + +const packageRoot = new URL('../', import.meta.url); +const manifest = JSON.parse(await readFile(new URL('package.json', packageRoot), 'utf8')); +const require = createRequire(import.meta.url); + +test('owns one codegen boundary for both native components and future modules', () => { + assert.equal(manifest.codegenConfig.name, 'FRNNativeCoreSpec'); + assert.equal(manifest.codegenConfig.type, 'all'); + assert.equal(manifest.codegenConfig.jsSrcsDir, 'src/specs'); + assert.notEqual(manifest.codegenConfig.includesGeneratedCode, true); + assert.deepEqual(manifest.codegenConfig.ios.componentProvider, { + Callout: 'RCTCalloutComponentView', + FocusZone: 'RCTFocusZoneComponentView', + }); + assert.deepEqual(manifest.codegenConfig.windows.generators, ['componentsWindows', 'modulesWindows']); +}); + +test('autolinks one Fabric-only Windows library registering both components', async () => { + const config = require('../react-native.config.cjs'); + assert.deepEqual(config.dependency.platforms.windows.projects, [ + { projectFile: 'NativeCore/NativeCore.vcxproj', directDependency: true }, + ]); + + const project = await readFile(new URL('windows/NativeCore/NativeCore.vcxproj', packageRoot), 'utf8'); + assert.match(project, /true<\/RnwNewArchOnly>/); + assert.match(project, /components\\Callout\\Callout\.cpp/); + assert.match(project, /components\\FocusZone\\FocusZoneComponentView\.cpp/); + const provider = await readFile(new URL('windows/NativeCore/ReactPackageProvider.cpp', packageRoot), 'utf8'); + assert.match(provider, /RegisterCalloutComponentView\(packageBuilder\)/); + assert.match(provider, /RegisterFocusZoneComponentView\(packageBuilder\)/); +}); + +test('the shared macOS pod includes both components and retains both renderer adapters', async () => { + const podspec = await readFile(new URL('FRNNativeCore.podspec', packageRoot), 'utf8'); + assert.match(podspec, /s\.name\s*=\s*'FRNNativeCore'/); + assert.match(podspec, /macos\/\*\*\/\*\.\{swift,h,m,mm\}/); + + for (const component of ['Callout', 'FocusZone']) { + const fabric = await readFile(new URL(`macos/components/${component}/fabric/RCT${component}ComponentView.mm`, packageRoot), 'utf8'); + assert.match(fabric, /react\/renderer\/components\/FRNNativeCoreSpec\/Props\.h/); + assert.ok((await readdir(new URL(`macos/components/${component}/paper/`, packageRoot))).length > 0); + assert.ok((await readdir(new URL(`macos/components/${component}/shared/`, packageRoot))).length > 0); + } +}); + +for (const shim of ['callout', 'focus-zone']) { + test(`${shim} is a JS-only compatibility shim with no duplicate native ownership`, async () => { + const root = new URL(`../../shim/${shim}/`, packageRoot); + const shimManifest = JSON.parse(await readFile(new URL('package.json', root), 'utf8')); + assert.equal(shimManifest.dependencies[manifest.name], 'workspace:*'); + assert.equal(shimManifest.codegenConfig, undefined); + assert.equal(shimManifest['react-native-windows'], undefined); + const files = await readdir(root); + assert.ok(!files.some((file) => file.endsWith('.podspec') || ['macos', 'windows', 'react-native.config.cjs'].includes(file))); + const entrypoint = await readFile(new URL('src/index.ts', root), 'utf8'); + assert.match(entrypoint, /from '@fluentui-react-native\/native-core'/); + assert.doesNotMatch(entrypoint, /export \*/); + }); +} diff --git a/packages/agentic/native-core/tsconfig.json b/packages/agentic/native-core/tsconfig.json index d952200ef0..bd2b34cb96 100644 --- a/packages/agentic/native-core/tsconfig.json +++ b/packages/agentic/native-core/tsconfig.json @@ -9,6 +9,12 @@ "include": ["src"], "exclude": ["**/*.stories.ts", "**/*.stories.tsx", "**/*.wdio.ts"], "references": [ + { + "path": "../../framework-base/tsconfig.json" + }, + { + "path": "../../utils/interactive-hooks/tsconfig.json" + }, { "path": "../../../scripts/tsconfig.json" } diff --git a/packages/components/FocusZone/windows/.gitignore b/packages/agentic/native-core/windows/.gitignore similarity index 100% rename from packages/components/FocusZone/windows/.gitignore rename to packages/agentic/native-core/windows/.gitignore diff --git a/packages/components/FocusZone/windows/ExperimentalFeatures.props b/packages/agentic/native-core/windows/ExperimentalFeatures.props similarity index 100% rename from packages/components/FocusZone/windows/ExperimentalFeatures.props rename to packages/agentic/native-core/windows/ExperimentalFeatures.props diff --git a/packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.def b/packages/agentic/native-core/windows/NativeCore/NativeCore.def similarity index 100% rename from packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.def rename to packages/agentic/native-core/windows/NativeCore/NativeCore.def diff --git a/packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.vcxproj b/packages/agentic/native-core/windows/NativeCore/NativeCore.vcxproj similarity index 94% rename from packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.vcxproj rename to packages/agentic/native-core/windows/NativeCore/NativeCore.vcxproj index 3d23cae505..ac701265e9 100644 --- a/packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.vcxproj +++ b/packages/agentic/native-core/windows/NativeCore/NativeCore.vcxproj @@ -6,9 +6,9 @@ true true {85648B33-7A31-434B-ACE5-6E4F6E52EBAD} - FRNFocusZone + FRNNativeCore Win32Proj - FRNFocusZone + FRNNativeCore 10.0.22621.0 en-US @@ -92,7 +92,7 @@ user32.lib;windowsapp.lib;%(AdditionalDependencies) Console true - FRNFocusZone.def + NativeCore.def @@ -107,16 +107,17 @@ - + + ReactPackageProvider.idl - - + + Create @@ -128,9 +129,6 @@ - - - diff --git a/packages/components/FocusZone/windows/FRNFocusZone/ReactPackageProvider.cpp b/packages/agentic/native-core/windows/NativeCore/ReactPackageProvider.cpp similarity index 59% rename from packages/components/FocusZone/windows/FRNFocusZone/ReactPackageProvider.cpp rename to packages/agentic/native-core/windows/NativeCore/ReactPackageProvider.cpp index ce6933d568..c215bf44f2 100644 --- a/packages/components/FocusZone/windows/FRNFocusZone/ReactPackageProvider.cpp +++ b/packages/agentic/native-core/windows/NativeCore/ReactPackageProvider.cpp @@ -5,20 +5,23 @@ #include "ReactPackageProvider.g.cpp" #endif -#include "FocusZoneComponentView.h" +#include "components/Callout/Callout.h" +#include "components/FocusZone/FocusZoneComponentView.h" using namespace winrt::Microsoft::ReactNative; -namespace winrt::FRNFocusZone::implementation +namespace winrt::FRNNativeCore::implementation { void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept { #ifdef RNW_NEW_ARCH + AddAttributedModules(packageBuilder, true); + RegisterCalloutComponentView(packageBuilder); RegisterFocusZoneComponentView(packageBuilder); #else UNREFERENCED_PARAMETER(packageBuilder); #endif } -} // namespace winrt::FRNFocusZone::implementation +} // namespace winrt::FRNNativeCore::implementation diff --git a/packages/components/FocusZone/windows/FRNFocusZone/ReactPackageProvider.h b/packages/agentic/native-core/windows/NativeCore/ReactPackageProvider.h similarity index 65% rename from packages/components/FocusZone/windows/FRNFocusZone/ReactPackageProvider.h rename to packages/agentic/native-core/windows/NativeCore/ReactPackageProvider.h index 1912fd12d3..5d81282ff7 100644 --- a/packages/components/FocusZone/windows/FRNFocusZone/ReactPackageProvider.h +++ b/packages/agentic/native-core/windows/NativeCore/ReactPackageProvider.h @@ -4,7 +4,7 @@ using namespace winrt::Microsoft::ReactNative; -namespace winrt::FRNFocusZone::implementation +namespace winrt::FRNNativeCore::implementation { struct ReactPackageProvider : ReactPackageProviderT @@ -14,11 +14,11 @@ struct ReactPackageProvider : ReactPackageProviderT void CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept; }; -} // namespace winrt::FRNFocusZone::implementation +} // namespace winrt::FRNNativeCore::implementation -namespace winrt::FRNFocusZone::factory_implementation +namespace winrt::FRNNativeCore::factory_implementation { struct ReactPackageProvider : ReactPackageProviderT {}; -} // namespace winrt::FRNFocusZone::factory_implementation +} // namespace winrt::FRNNativeCore::factory_implementation diff --git a/packages/components/FocusZone/windows/FRNFocusZone/ReactPackageProvider.idl b/packages/agentic/native-core/windows/NativeCore/ReactPackageProvider.idl similarity index 87% rename from packages/components/FocusZone/windows/FRNFocusZone/ReactPackageProvider.idl rename to packages/agentic/native-core/windows/NativeCore/ReactPackageProvider.idl index f8a01091d5..7850c2224c 100644 --- a/packages/components/FocusZone/windows/FRNFocusZone/ReactPackageProvider.idl +++ b/packages/agentic/native-core/windows/NativeCore/ReactPackageProvider.idl @@ -1,4 +1,4 @@ -namespace FRNFocusZone +namespace FRNNativeCore { [webhosthidden] [default_interface] diff --git a/packages/components/FocusZone/windows/FRNFocusZone/codegen/.clang-format b/packages/agentic/native-core/windows/NativeCore/codegen/.clang-format similarity index 50% rename from packages/components/FocusZone/windows/FRNFocusZone/codegen/.clang-format rename to packages/agentic/native-core/windows/NativeCore/codegen/.clang-format index 9d159247d5..a43d914ec3 100644 --- a/packages/components/FocusZone/windows/FRNFocusZone/codegen/.clang-format +++ b/packages/agentic/native-core/windows/NativeCore/codegen/.clang-format @@ -1,2 +1,2 @@ DisableFormat: true -SortIncludes: false +SortIncludes: false \ No newline at end of file diff --git a/packages/native/Callout/windows/Callout/codegen/react/components/FRNCalloutSpec/Callout.g.h b/packages/agentic/native-core/windows/NativeCore/codegen/react/components/FRNNativeCoreSpec/Callout.g.h similarity index 99% rename from packages/native/Callout/windows/Callout/codegen/react/components/FRNCalloutSpec/Callout.g.h rename to packages/agentic/native-core/windows/NativeCore/codegen/react/components/FRNNativeCoreSpec/Callout.g.h index ef6731db34..072434e2c0 100644 --- a/packages/native/Callout/windows/Callout/codegen/react/components/FRNCalloutSpec/Callout.g.h +++ b/packages/agentic/native-core/windows/NativeCore/codegen/react/components/FRNNativeCoreSpec/Callout.g.h @@ -16,7 +16,7 @@ #ifdef RNW_NEW_ARCH -namespace winrt::FluentUI::Callout::Codegen { +namespace FRNNativeCoreCodegen { REACT_STRUCT(CalloutSpec_CalloutProps_anchorRect) struct CalloutSpec_CalloutProps_anchorRect { @@ -370,6 +370,6 @@ void RegisterCalloutNativeComponent( }); } -} // namespace winrt::FluentUI::Callout::Codegen +} // namespace FRNNativeCoreCodegen #endif // #ifdef RNW_NEW_ARCH diff --git a/packages/components/FocusZone/windows/FRNFocusZone/codegen/react/components/FRNFocusZoneSpec/FocusZone.g.h b/packages/agentic/native-core/windows/NativeCore/codegen/react/components/FRNNativeCoreSpec/FocusZone.g.h similarity index 99% rename from packages/components/FocusZone/windows/FRNFocusZone/codegen/react/components/FRNFocusZoneSpec/FocusZone.g.h rename to packages/agentic/native-core/windows/NativeCore/codegen/react/components/FRNNativeCoreSpec/FocusZone.g.h index da24d5e0cd..e6e2080614 100644 --- a/packages/components/FocusZone/windows/FRNFocusZone/codegen/react/components/FRNFocusZoneSpec/FocusZone.g.h +++ b/packages/agentic/native-core/windows/NativeCore/codegen/react/components/FRNNativeCoreSpec/FocusZone.g.h @@ -16,7 +16,7 @@ #ifdef RNW_NEW_ARCH -namespace FRNFocusZoneCodegen { +namespace FRNNativeCoreCodegen { REACT_STRUCT(FocusZoneProps) struct FocusZoneProps : winrt::implements { @@ -238,6 +238,6 @@ void RegisterFocusZoneNativeComponent( }); } -} // namespace FRNFocusZoneCodegen +} // namespace FRNNativeCoreCodegen #endif // #ifdef RNW_NEW_ARCH diff --git a/packages/native/Callout/windows/Callout/Callout.cpp b/packages/agentic/native-core/windows/NativeCore/components/Callout/Callout.cpp similarity index 100% rename from packages/native/Callout/windows/Callout/Callout.cpp rename to packages/agentic/native-core/windows/NativeCore/components/Callout/Callout.cpp diff --git a/packages/native/Callout/windows/Callout/Callout.h b/packages/agentic/native-core/windows/NativeCore/components/Callout/Callout.h similarity index 86% rename from packages/native/Callout/windows/Callout/Callout.h rename to packages/agentic/native-core/windows/NativeCore/components/Callout/Callout.h index 4a434fbfa9..41b5ffb623 100644 --- a/packages/native/Callout/windows/Callout/Callout.h +++ b/packages/agentic/native-core/windows/NativeCore/components/Callout/Callout.h @@ -6,7 +6,7 @@ #include "NativeModules.h" -#include "codegen/react/components/FRNCalloutSpec/Callout.g.h" +#include "codegen/react/components/FRNNativeCoreSpec/Callout.g.h" #include #include @@ -19,6 +19,8 @@ namespace winrt::FluentUI::Callout { +namespace Codegen = ::FRNNativeCoreCodegen; + } // namespace winrt::FluentUI::Callout void RegisterCalloutComponentView( diff --git a/packages/components/FocusZone/windows/FRNFocusZone/FocusZoneComponentView.cpp b/packages/agentic/native-core/windows/NativeCore/components/FocusZone/FocusZoneComponentView.cpp similarity index 98% rename from packages/components/FocusZone/windows/FRNFocusZone/FocusZoneComponentView.cpp rename to packages/agentic/native-core/windows/NativeCore/components/FocusZone/FocusZoneComponentView.cpp index f75854d11e..b373546a62 100644 --- a/packages/components/FocusZone/windows/FRNFocusZone/FocusZoneComponentView.cpp +++ b/packages/agentic/native-core/windows/NativeCore/components/FocusZone/FocusZoneComponentView.cpp @@ -4,7 +4,7 @@ #ifdef RNW_NEW_ARCH -#include "codegen/react/components/FRNFocusZoneSpec/FocusZone.g.h" +#include "codegen/react/components/FRNNativeCoreSpec/FocusZone.g.h" namespace winrt::FRNFocusZone { @@ -29,7 +29,7 @@ void CollectFocusableDescendants( struct FocusZoneComponentView : winrt::implements, - FRNFocusZoneCodegen::BaseFocusZone { + FRNNativeCoreCodegen::BaseFocusZone { void Initialize(const winrtRN::ComponentView &view) noexcept override { m_containerTag = view.Tag(); @@ -586,7 +586,7 @@ struct FocusZoneComponentView void RegisterFocusZoneComponentView(const winrt::Microsoft::ReactNative::IReactPackageBuilder &packageBuilder) noexcept { using ComponentView = winrt::FRNFocusZone::FocusZoneComponentView; - FRNFocusZoneCodegen::RegisterFocusZoneNativeComponent(packageBuilder, {}); + FRNNativeCoreCodegen::RegisterFocusZoneNativeComponent(packageBuilder, {}); } #endif diff --git a/packages/components/FocusZone/windows/FRNFocusZone/FocusZoneComponentView.h b/packages/agentic/native-core/windows/NativeCore/components/FocusZone/FocusZoneComponentView.h similarity index 100% rename from packages/components/FocusZone/windows/FRNFocusZone/FocusZoneComponentView.h rename to packages/agentic/native-core/windows/NativeCore/components/FocusZone/FocusZoneComponentView.h diff --git a/packages/components/FocusZone/windows/FRNFocusZone/packages.lock.json b/packages/agentic/native-core/windows/NativeCore/packages.lock.json similarity index 100% rename from packages/components/FocusZone/windows/FRNFocusZone/packages.lock.json rename to packages/agentic/native-core/windows/NativeCore/packages.lock.json diff --git a/packages/components/FocusZone/windows/FRNFocusZone/pch.cpp b/packages/agentic/native-core/windows/NativeCore/pch.cpp similarity index 100% rename from packages/components/FocusZone/windows/FRNFocusZone/pch.cpp rename to packages/agentic/native-core/windows/NativeCore/pch.cpp diff --git a/packages/components/FocusZone/windows/FRNFocusZone/pch.h b/packages/agentic/native-core/windows/NativeCore/pch.h similarity index 100% rename from packages/components/FocusZone/windows/FRNFocusZone/pch.h rename to packages/agentic/native-core/windows/NativeCore/pch.h diff --git a/packages/components/FocusZone/windows/FRNFocusZone/targetver.h b/packages/agentic/native-core/windows/NativeCore/targetver.h similarity index 100% rename from packages/components/FocusZone/windows/FRNFocusZone/targetver.h rename to packages/agentic/native-core/windows/NativeCore/targetver.h diff --git a/packages/components/FocusZone/windows/NuGet.Config b/packages/agentic/native-core/windows/NuGet.Config similarity index 100% rename from packages/components/FocusZone/windows/NuGet.Config rename to packages/agentic/native-core/windows/NuGet.Config diff --git a/packages/agentic/storybook-desktop-runtime/package.json b/packages/agentic/storybook-desktop-runtime/package.json index 51388d9ae6..0272613670 100644 --- a/packages/agentic/storybook-desktop-runtime/package.json +++ b/packages/agentic/storybook-desktop-runtime/package.json @@ -31,9 +31,9 @@ "test": "fluentui-scripts jest" }, "dependencies": { - "@fluentui-react-native/callout": "workspace:*", "@fluentui-react-native/default-theme": "workspace:*", "@fluentui-react-native/design": "workspace:*", + "@fluentui-react-native/native-core": "workspace:*", "@rnx-kit/metro-config": "catalog:", "@rnx-kit/metro-resolver-symlinks": "catalog:", "@storybook/react-native": "^10.4.7", @@ -85,7 +85,7 @@ "jestPlatform": "macos", "knip": { "ignoreDependencies": [ - "@fluentui-react-native/callout" + "@fluentui-react-native/native-core" ] } }, diff --git a/packages/agentic/storybook-desktop-runtime/src/Win32CalloutPortal.tsx b/packages/agentic/storybook-desktop-runtime/src/Win32CalloutPortal.tsx index 1d1e36b211..af9f3eceb7 100644 --- a/packages/agentic/storybook-desktop-runtime/src/Win32CalloutPortal.tsx +++ b/packages/agentic/storybook-desktop-runtime/src/Win32CalloutPortal.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; -import { Callout } from '@fluentui-react-native/callout'; -import type { CalloutProps, DirectionalHint } from '@fluentui-react-native/callout'; +import { Callout } from '@fluentui-react-native/native-core'; +import type { CalloutProps, DirectionalHint } from '@fluentui-react-native/native-core'; import { RootInputBoundary } from '@fluentui-react-native/design'; import { useTheme } from '@storybook/react-native-theming'; diff --git a/packages/agentic/storybook-desktop-runtime/tsconfig.json b/packages/agentic/storybook-desktop-runtime/tsconfig.json index d7156beca4..2fe2c0c71f 100644 --- a/packages/agentic/storybook-desktop-runtime/tsconfig.json +++ b/packages/agentic/storybook-desktop-runtime/tsconfig.json @@ -8,15 +8,15 @@ }, "include": ["src"], "references": [ - { - "path": "../../native/Callout/tsconfig.json" - }, { "path": "../../theming/default-theme/tsconfig.json" }, { "path": "../design/tsconfig.json" }, + { + "path": "../native-core/tsconfig.json" + }, { "path": "../../../scripts/tsconfig.json" } diff --git a/packages/agentic/storybook-desktop/src/config/makeDesktopStorybookConfig.test.ts b/packages/agentic/storybook-desktop/src/config/makeDesktopStorybookConfig.test.ts index dcf4c56e28..49cead7354 100644 --- a/packages/agentic/storybook-desktop/src/config/makeDesktopStorybookConfig.test.ts +++ b/packages/agentic/storybook-desktop/src/config/makeDesktopStorybookConfig.test.ts @@ -20,7 +20,7 @@ function makeAgenticConfig() { }, }, ], - '@fluentui-react-native/callout', + '@fluentui-react-native/native-core', ], }); } @@ -40,7 +40,7 @@ describe('DesktopStorybookConfig', () => { const config = makeAgenticConfig(); const packages = config.getStoryPackages('macos'); - expect(packages.map(({ name }) => name)).toEqual(['@fluentui-react-native/components', '@fluentui-react-native/callout']); + expect(packages.map(({ name }) => name)).toEqual(['@fluentui-react-native/components', '@fluentui-react-native/native-core']); expect(packages[0].root).toBe(path.resolve(storybookRoot, '../../packages/agentic/components')); expect(packages[0].storyPatterns).toEqual(['src/**/*.stories.?(ts|tsx)']); }); @@ -50,19 +50,19 @@ describe('DesktopStorybookConfig', () => { expect(config.getStoryGlobs('macos')).toEqual([ '../../../packages/agentic/components/src/**/*.stories.?(ts|tsx)', - '../../../packages/native/Callout/src/**/*.stories.?(ts|tsx)', + '../../../packages/agentic/native-core/src/**/*.stories.?(ts|tsx)', ]); expect(config.getStoryGlobs('win32')).toEqual([ '../../../packages/agentic/components/src/primitives/**/*.stories.?(ts|tsx)', '../../../packages/agentic/components/src/components/!(accordion|list-item)/**/*.stories.?(ts|tsx)', - '../../../packages/native/Callout/src/**/*.stories.?(ts|tsx)', + '../../../packages/agentic/native-core/src/**/*.stories.?(ts|tsx)', ]); }); test('filters packages by platform and applies addon defaults', () => { const config = makeDesktopStorybookConfig({ projectRoot: storybookRoot, - storyPackages: [['@fluentui-react-native/callout', { platforms: ['macos'] }]], + storyPackages: [['@fluentui-react-native/native-core', { platforms: ['macos'] }]], }); expect(config.getStorybookConfig('windows')).toMatchObject({ diff --git a/packages/components/ContextualMenu/tsconfig.json b/packages/components/ContextualMenu/tsconfig.json index 2ab212fd85..753669edfc 100644 --- a/packages/components/ContextualMenu/tsconfig.json +++ b/packages/components/ContextualMenu/tsconfig.json @@ -12,13 +12,13 @@ "path": "../../utils/adapters/tsconfig.json" }, { - "path": "../../native/Callout/tsconfig.json" + "path": "../../shim/callout/tsconfig.json" }, { "path": "../../agentic/design/tsconfig.json" }, { - "path": "../FocusZone/tsconfig.json" + "path": "../../shim/focus-zone/tsconfig.json" }, { "path": "../../framework-base/tsconfig.json" diff --git a/packages/components/FocusZone/RCTFocusZone.podspec b/packages/components/FocusZone/RCTFocusZone.podspec deleted file mode 100644 index 13ae57174e..0000000000 --- a/packages/components/FocusZone/RCTFocusZone.podspec +++ /dev/null @@ -1,21 +0,0 @@ -require 'json' - -package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) - -Pod::Spec.new do |s| - s.name = 'RCTFocusZone' - s.version = package['version'] - s.summary = package['description'] - s.license = package['license'] - - s.authors = package['author'] - s.homepage = "https://github.com/microsoft/fluentui-react-native" - - s.source = { :git => "https://github.com/microsoft/fluentui-react-native.git", :tag => "#{s.version}" } - s.swift_version = "5" - - s.osx.deployment_target = "10.15" - s.osx.source_files = "macos/*.{swift,h,m,mm}" - - install_modules_dependencies(s) -end diff --git a/packages/components/FocusZone/react-native.config.cjs b/packages/components/FocusZone/react-native.config.cjs deleted file mode 100644 index e6858db429..0000000000 --- a/packages/components/FocusZone/react-native.config.cjs +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - dependency: { - platforms: { - windows: { - sourceDir: 'windows', - projects: [ - { - projectFile: 'FRNFocusZone/FRNFocusZone.vcxproj', - directDependency: true, - }, - ], - }, - }, - }, -}; diff --git a/packages/components/FocusZone/windows/FRNFocusZone.sln b/packages/components/FocusZone/windows/FRNFocusZone.sln deleted file mode 100644 index 08d4bc6912..0000000000 --- a/packages/components/FocusZone/windows/FRNFocusZone.sln +++ /dev/null @@ -1,43 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.3.32929.385 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FRNFocusZone", "FRNFocusZone\FRNFocusZone.vcxproj", "{85648B33-7A31-434B-ACE5-6E4F6E52EBAD}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Debug|ARM64 = Debug|ARM64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - Release|ARM64 = Release|ARM64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Debug|x64.ActiveCfg = Debug|x64 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Debug|x64.Build.0 = Debug|x64 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Debug|x64.Deploy.0 = Debug|x64 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Debug|x86.ActiveCfg = Debug|Win32 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Debug|x86.Build.0 = Debug|Win32 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Debug|x86.Deploy.0 = Debug|Win32 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Debug|ARM64.Build.0 = Debug|ARM64 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Debug|ARM64.Deploy.0 = Debug|ARM64 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Release|x64.ActiveCfg = Release|x64 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Release|x64.Build.0 = Release|x64 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Release|x64.Deploy.0 = Release|x64 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Release|x86.ActiveCfg = Release|Win32 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Release|x86.Build.0 = Release|Win32 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Release|x86.Deploy.0 = Release|Win32 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Release|ARM64.ActiveCfg = Release|ARM64 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Release|ARM64.Build.0 = Release|ARM64 - {85648B33-7A31-434B-ACE5-6E4F6E52EBAD}.Release|ARM64.Deploy.0 = Release|ARM64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {CA064327-BA5B-4132-B77B-23C9DDDF9E92} - EndGlobalSection -EndGlobal diff --git a/packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.rc b/packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.rc deleted file mode 100644 index 3212c105fce3ed3e784a1f88d04cd4b26a0d7f4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmZXNK?;LF3`O6%&^rveEI5bgx?A_6wiU#d(Ye0ubG8z~PhNhK-yVqs9!&HkKCI@G zP1ed*G99zsotqJOF|wFRzNv#OsmETn>aBy_ac$zOr{T)QFR^GXU7XIkhvKbH`wefZ G;{F4P;Ty#O diff --git a/packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.vcxproj.filters b/packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.vcxproj.filters deleted file mode 100644 index b4602ae41c..0000000000 --- a/packages/components/FocusZone/windows/FRNFocusZone/FRNFocusZone.vcxproj.filters +++ /dev/null @@ -1,44 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - - - Resource Files - - - \ No newline at end of file diff --git a/packages/components/FocusZone/windows/FRNFocusZone/resource.h b/packages/components/FocusZone/windows/FRNFocusZone/resource.h deleted file mode 100644 index fe63e7c1ad..0000000000 --- a/packages/components/FocusZone/windows/FRNFocusZone/resource.h +++ /dev/null @@ -1,5 +0,0 @@ -// -// Microsoft Visual C++ generated include file. -// Used by FRNFocusZone.rc - -#pragma once diff --git a/packages/components/Menu/tsconfig.json b/packages/components/Menu/tsconfig.json index 72d42549d5..36b2d5eb8f 100644 --- a/packages/components/Menu/tsconfig.json +++ b/packages/components/Menu/tsconfig.json @@ -15,13 +15,13 @@ "path": "../Button/tsconfig.json" }, { - "path": "../../native/Callout/tsconfig.json" + "path": "../../shim/callout/tsconfig.json" }, { "path": "../../agentic/design/tsconfig.json" }, { - "path": "../FocusZone/tsconfig.json" + "path": "../../shim/focus-zone/tsconfig.json" }, { "path": "../../framework-base/tsconfig.json" diff --git a/packages/components/RadioGroup/tsconfig.json b/packages/components/RadioGroup/tsconfig.json index 82457146a4..dbcd3d438c 100644 --- a/packages/components/RadioGroup/tsconfig.json +++ b/packages/components/RadioGroup/tsconfig.json @@ -15,7 +15,7 @@ "path": "../../agentic/design/tsconfig.json" }, { - "path": "../FocusZone/tsconfig.json" + "path": "../../shim/focus-zone/tsconfig.json" }, { "path": "../../framework-base/tsconfig.json" diff --git a/packages/components/TabList/tsconfig.json b/packages/components/TabList/tsconfig.json index 1644446a07..137360d774 100644 --- a/packages/components/TabList/tsconfig.json +++ b/packages/components/TabList/tsconfig.json @@ -15,7 +15,7 @@ "path": "../../agentic/design/tsconfig.json" }, { - "path": "../FocusZone/tsconfig.json" + "path": "../../shim/focus-zone/tsconfig.json" }, { "path": "../../framework-base/tsconfig.json" diff --git a/packages/deprecated/foundation-compose/AGENTS.md b/packages/deprecated/foundation-compose/AGENTS.md index 0ef61601ae..8a179372b2 100644 --- a/packages/deprecated/foundation-compose/AGENTS.md +++ b/packages/deprecated/foundation-compose/AGENTS.md @@ -147,7 +147,7 @@ automatically. `ref` during preparation, commonly with `useViewCommandFocus`; type ref-bearing roots with `React.PropsWithRef<...>`. For imperative native APIs, use an internal native ref plus `useImperativeHandle`, as in - `packages/native/Callout/src/Callout.tsx`. + `packages/agentic/native-core/src/components/callout/Callout.tsx`. - Do not introduce `forwardRef` or a new public `ref` convention into an established v0 API without an explicit migration requirement. diff --git a/packages/experimental/Dropdown/tsconfig.json b/packages/experimental/Dropdown/tsconfig.json index d7fb6048b8..a6dbd1a030 100644 --- a/packages/experimental/Dropdown/tsconfig.json +++ b/packages/experimental/Dropdown/tsconfig.json @@ -15,7 +15,7 @@ "path": "../../components/Button/tsconfig.json" }, { - "path": "../../native/Callout/tsconfig.json" + "path": "../../shim/callout/tsconfig.json" }, { "path": "../../agentic/design/tsconfig.json" diff --git a/packages/experimental/Tooltip/tsconfig.json b/packages/experimental/Tooltip/tsconfig.json index 011332a5de..db3204acba 100644 --- a/packages/experimental/Tooltip/tsconfig.json +++ b/packages/experimental/Tooltip/tsconfig.json @@ -12,7 +12,7 @@ "path": "../../components/Button/tsconfig.json" }, { - "path": "../../native/Callout/tsconfig.json" + "path": "../../shim/callout/tsconfig.json" }, { "path": "../../framework-base/tsconfig.json" diff --git a/packages/libraries/core/tsconfig.json b/packages/libraries/core/tsconfig.json index 5bd6e0487c..b675c0d1e4 100644 --- a/packages/libraries/core/tsconfig.json +++ b/packages/libraries/core/tsconfig.json @@ -21,7 +21,7 @@ "path": "../../components/Button/tsconfig.json" }, { - "path": "../../native/Callout/tsconfig.json" + "path": "../../shim/callout/tsconfig.json" }, { "path": "../../components/Checkbox/tsconfig.json" @@ -33,7 +33,7 @@ "path": "../../components/FocusTrapZone/tsconfig.json" }, { - "path": "../../components/FocusZone/tsconfig.json" + "path": "../../shim/focus-zone/tsconfig.json" }, { "path": "../../utils/interactive-hooks/tsconfig.json" diff --git a/packages/native/Callout/windows/.gitignore b/packages/native/Callout/windows/.gitignore deleted file mode 100644 index 0fd0acfc6d..0000000000 --- a/packages/native/Callout/windows/.gitignore +++ /dev/null @@ -1,46 +0,0 @@ -*AppPackages* -*BundleArtifacts* - -#OS junk files -[Tt]humbs.db -*.DS_Store - -#Visual Studio files -*.[Oo]bj -*.user -*.aps -*.pch -*.vspscc -*.vssscc -*_i.c -*_p.c -*.ncb -*.suo -*.tlb -*.tlh -*.bak -*.[Cc]ache -*.ilk -*.log -*.lib -*.sbr -*.sdf -*.opensdf -*.opendb -*.unsuccessfulbuild -ipch/ -[Oo]bj/ -[Bb]in -[Dd]ebug*/ -[Rr]elease*/ -Ankh.NoLoad -.vs/ -# Visual C++ cache files - -#Files generated by the VS build -**/Generated Files/** - -#Files generated by MS build -*.binlog -*.err -*.wrn diff --git a/packages/native/Callout/windows/Callout/Callout.def b/packages/native/Callout/windows/Callout/Callout.def deleted file mode 100644 index 24e7c1235c..0000000000 --- a/packages/native/Callout/windows/Callout/Callout.def +++ /dev/null @@ -1,3 +0,0 @@ -EXPORTS -DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE -DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/packages/native/Callout/windows/Callout/Callout.vcxproj b/packages/native/Callout/windows/Callout/Callout.vcxproj deleted file mode 100644 index 88efc1ff9b..0000000000 --- a/packages/native/Callout/windows/Callout/Callout.vcxproj +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - true - true - true - {FE36524A-FE8C-456A-9368-08FB1EB16CF8} - FluentUICallout - FluentUI.Callout - en-US - 16.0 - false - Windows Store - 10.0 - 10.0.19041.0 - 10.0.17763.0 - - - $([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\ - true - - - - - - Debug - ARM - - - Debug - ARM64 - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - ARM64 - - - Release - Win32 - - - Release - x64 - - - - DynamicLibrary - Unicode - false - - - true - true - - - false - true - false - - - - - - - - - - - - - - Use - pch.h - $(IntDir)pch.pch - Level4 - %(AdditionalOptions) /bigobj - 4453;28204 - _WINRT_DLL;%(PreprocessorDefinitions) - $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) - - - User32.lib;%(AdditionalDependencies) - Console - true - Callout.def - - - - - _DEBUG;%(PreprocessorDefinitions) - - - - - NDEBUG;%(PreprocessorDefinitions) - - - - - false - - - - - - ReactPackageProvider.idl - - - - - - - Create - - - ReactPackageProvider.idl - - - - - - - - - - - - - - - - - This project references targets in your node_modules\react-native-windows folder that are missing. The missing file is {0}. - - - - - diff --git a/packages/native/Callout/windows/Callout/ReactPackageProvider.cpp b/packages/native/Callout/windows/Callout/ReactPackageProvider.cpp deleted file mode 100644 index bac1881140..0000000000 --- a/packages/native/Callout/windows/Callout/ReactPackageProvider.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" - -#include "ReactPackageProvider.h" -#if __has_include("ReactPackageProvider.g.cpp") -#include "ReactPackageProvider.g.cpp" -#endif - -#include "Callout.h" - -using namespace winrt::Microsoft::ReactNative; - -namespace winrt::FluentUI::Callout::implementation { - -void ReactPackageProvider::CreatePackage( - IReactPackageBuilder const &packageBuilder) noexcept { - AddAttributedModules(packageBuilder, true); - -#ifdef RNW_NEW_ARCH - RegisterCalloutComponentView(packageBuilder); -#endif // #ifdef RNW_NEW_ARCH -} - -} // namespace winrt::FluentUI::Callout::implementation diff --git a/packages/native/Callout/windows/Callout/ReactPackageProvider.h b/packages/native/Callout/windows/Callout/ReactPackageProvider.h deleted file mode 100644 index ee4a609103..0000000000 --- a/packages/native/Callout/windows/Callout/ReactPackageProvider.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once - -#include "ReactPackageProvider.g.h" - -using namespace winrt::Microsoft::ReactNative; - -namespace winrt::FluentUI::Callout::implementation { - -struct ReactPackageProvider : ReactPackageProviderT { - ReactPackageProvider() = default; - - void CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept; -}; - -} // namespace winrt::FluentUI::Callout::implementation - -namespace winrt::FluentUI::Callout::factory_implementation { - -struct ReactPackageProvider - : ReactPackageProviderT {}; - -} // namespace winrt::FluentUI::Callout::factory_implementation diff --git a/packages/native/Callout/windows/Callout/ReactPackageProvider.idl b/packages/native/Callout/windows/Callout/ReactPackageProvider.idl deleted file mode 100644 index cd726b6e61..0000000000 --- a/packages/native/Callout/windows/Callout/ReactPackageProvider.idl +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace FluentUI.Callout -{ - [webhosthidden] - [default_interface] - runtimeclass ReactPackageProvider : Microsoft.ReactNative.IReactPackageProvider - { - ReactPackageProvider(); - }; -} diff --git a/packages/native/Callout/windows/Callout/codegen/.clang-format b/packages/native/Callout/windows/Callout/codegen/.clang-format deleted file mode 100644 index 9d159247d5..0000000000 --- a/packages/native/Callout/windows/Callout/codegen/.clang-format +++ /dev/null @@ -1,2 +0,0 @@ -DisableFormat: true -SortIncludes: false diff --git a/packages/native/Callout/windows/Callout/packages.lock.json b/packages/native/Callout/windows/Callout/packages.lock.json deleted file mode 100644 index 396b4e4c7a..0000000000 --- a/packages/native/Callout/windows/Callout/packages.lock.json +++ /dev/null @@ -1,151 +0,0 @@ -{ - "version": 1, - "dependencies": { - "native,Version=v0.0": { - "boost": { - "type": "Direct", - "requested": "[1.84.0, )", - "resolved": "1.84.0", - "contentHash": "4el2YP3cNJDVFPdzOso+LxGvdWP2rHxML4siq8VdonNypW2m4q503tHfCj6vK0L1UfxioE2hpFGb4ITEua73tg==" - }, - "Microsoft.ReactNative": { - "type": "Direct", - "requested": "[0.81.35, )", - "resolved": "0.81.35", - "contentHash": "WjsJFALaLLUvJHUBw8k8G8/PB3p5qvqqXG7NYJefHOgqB7MbCSOnD/bckIqKcp53J+MkPxqaeg5tnYLVTVhqwQ==" - }, - "Microsoft.ReactNative.Cxx": { - "type": "Direct", - "requested": "[0.81.35, )", - "resolved": "0.81.35", - "contentHash": "oadpIMSLN11M9W/hyZKyiCKE2wMvjb80Bl5jInVUmqDJWNswxnu7W5Kt/NA9ciy+BN0ru62ucO0E2PJDrnP/Jg==", - "dependencies": { - "Microsoft.ReactNative": "0.81.35" - } - }, - "Microsoft.VCRTForwarders.140": { - "type": "Direct", - "requested": "[1.0.2-rc, )", - "resolved": "1.0.2-rc", - "contentHash": "/r+sjtEeCIGyDhobIZ5hSmYhC/dSyGZxf1SxYJpElUhB0LMCktOMFs9gXrauXypIFECpVynNyVjAmJt6hjJ5oQ==" - }, - "Microsoft.Windows.CppWinRT": { - "type": "Direct", - "requested": "[2.0.230706.1, )", - "resolved": "2.0.230706.1", - "contentHash": "l0D7oCw/5X+xIKHqZTi62TtV+1qeSz7KVluNFdrJ9hXsst4ghvqQ/Yhura7JqRdZWBXAuDS0G0KwALptdoxweQ==" - }, - "Microsoft.WindowsAppSDK": { - "type": "Direct", - "requested": "[1.8.260508005, )", - "resolved": "1.8.260508005", - "contentHash": "+aA+zrvqJKgsn/1TPOSR0Uy7dkMO5jI/+cDWPu2pWmXe4KQxYkR8gQFY9IrfbgCxxSd/yl1zMcAKD/4HBbNqaw==", - "dependencies": { - "Microsoft.WindowsAppSDK.AI": "[1.8.76]", - "Microsoft.WindowsAppSDK.Base": "[1.8.251216001]", - "Microsoft.WindowsAppSDK.DWrite": "[1.8.25122902]", - "Microsoft.WindowsAppSDK.Foundation": "[1.8.260505001]", - "Microsoft.WindowsAppSDK.InteractiveExperiences": "[1.8.260430001]", - "Microsoft.WindowsAppSDK.ML": "[1.8.2197]", - "Microsoft.WindowsAppSDK.Runtime": "[1.8.260508005]", - "Microsoft.WindowsAppSDK.Widgets": "[1.8.251231004]", - "Microsoft.WindowsAppSDK.WinUI": "[1.8.260505002]" - } - }, - "Microsoft.Web.WebView2": { - "type": "Transitive", - "resolved": "1.0.3179.45", - "contentHash": "3pokSH5CnN0G6rGhGFo1y87inxYhNxBQ2Vdf0wlvBj99KHxQJormjDACmqRnFeUsmuNFIhWwfAL1ztq7wD5qRA==" - }, - "Microsoft.Windows.SDK.BuildTools": { - "type": "Transitive", - "resolved": "10.0.26100.4654", - "contentHash": "2mgcOlj/t2RfSyyw+pVESfO+Tk1RkfQzto9Vrq42M1lUQIfQEwbi8QLha9GXWIOj+TFzeHIEJckIoF25mgiM8A==" - }, - "Microsoft.Windows.SDK.BuildTools.MSIX": { - "type": "Transitive", - "resolved": "1.7.20250829.1", - "contentHash": "IMdvRmCIZnBS5GkYnv0po1bcx6U1OF39pqA4TphQ9evDzpCRoSE19/PkDvlUNNrBavTsLIEJgd/TAIFner75ow==" - }, - "Microsoft.WindowsAppSDK.AI": { - "type": "Transitive", - "resolved": "1.8.76", - "contentHash": "Ayn9QybcwzH+c8eQlE7dm2oO3Jrcn2uohLcsHJpCbLOAfcisjzwtSBe0oyulbaJ86R4eDSX3RDS25tsjGpIqyQ==", - "dependencies": { - "Microsoft.WindowsAppSDK.Base": "1.8.251216001", - "Microsoft.WindowsAppSDK.Foundation": "1.8.260501000" - } - }, - "Microsoft.WindowsAppSDK.Base": { - "type": "Transitive", - "resolved": "1.8.251216001", - "contentHash": "PS1wriuFknz3W2F2P/e6RvOTM35w89Lsj/f0QmUEPrJjKnc+jM0JLX1vfdytI14y1gNRUTm9uclwP0aH/SVU5w==", - "dependencies": { - "Microsoft.Windows.SDK.BuildTools": "10.0.26100.4654", - "Microsoft.Windows.SDK.BuildTools.MSIX": "1.7.20250829.1" - } - }, - "Microsoft.WindowsAppSDK.DWrite": { - "type": "Transitive", - "resolved": "1.8.25122902", - "contentHash": "zFNn07i7Cyz62Y8FnPQAyzeZK7ww3m9t42i9pzy4C04pNbyUDQ4fG7pB6VSh6n4EyFuYtuFQuDzt4mKmXFrkrg==", - "dependencies": { - "Microsoft.WindowsAppSDK.Base": "1.8.251216001" - } - }, - "Microsoft.WindowsAppSDK.Foundation": { - "type": "Transitive", - "resolved": "1.8.260505001", - "contentHash": "41SSoEn3sKKCAVPA/w18zVJwV1C7aDEkPS2f6Zyp19m27tDEmteEp0XS3Ln3b0ElYR4FfPEPvIDbJPSA9vePGw==", - "dependencies": { - "Microsoft.WindowsAppSDK.Base": "1.8.251216001", - "Microsoft.WindowsAppSDK.InteractiveExperiences": "1.8.260430001" - } - }, - "Microsoft.WindowsAppSDK.InteractiveExperiences": { - "type": "Transitive", - "resolved": "1.8.260430001", - "contentHash": "fTPCnQb3ZarMh9khlEfbLDllcZzK0tSP+2S6W/T4cPyLLSAmARm8Gd3AC5kqubnDjzTvG+1lNZ1bZGFsIHplJQ==", - "dependencies": { - "Microsoft.WindowsAppSDK.Base": "1.8.251216001" - } - }, - "Microsoft.WindowsAppSDK.ML": { - "type": "Transitive", - "resolved": "1.8.2197", - "contentHash": "6Bc1SOLd5HicY3GbF+zr76YBfH4iZOKeEGxTK/lHKAK2ExZTWavOADxB2CKSi/irF4dWSngUdRFopWPmckJ6fA==", - "dependencies": { - "Microsoft.WindowsAppSDK.Base": "1.8.251216001", - "Microsoft.WindowsAppSDK.Foundation": "1.8.260505001" - } - }, - "Microsoft.WindowsAppSDK.Runtime": { - "type": "Transitive", - "resolved": "1.8.260508005", - "contentHash": "2JqXzA4heHSkkaXJNyEvYtpv2wsEUMhwQzgXmZsFmYSkUXSdeVc6hdHcWoBK1SY9l1Sjd1brLt7h16kIpgh2kA==", - "dependencies": { - "Microsoft.WindowsAppSDK.Base": "1.8.251216001" - } - }, - "Microsoft.WindowsAppSDK.Widgets": { - "type": "Transitive", - "resolved": "1.8.251231004", - "contentHash": "bIWqQYR8DCoB1SoPOMil5AtgtkTn438wJTdpsHgyO/6o7Eh7PMP5BzrR0KbDsFqy+4LhPWQ4vtwko5k93fECcA==", - "dependencies": { - "Microsoft.WindowsAppSDK.Base": "1.8.251216001" - } - }, - "Microsoft.WindowsAppSDK.WinUI": { - "type": "Transitive", - "resolved": "1.8.260505002", - "contentHash": "/hGl6EOmo8aeJR2bYbOmGhOicnJK4EY+u+cQI+jm8H1uXThfMX32DsfKOt9LkOcu8AKX3j5FpiBay8ObUSytIw==", - "dependencies": { - "Microsoft.Web.WebView2": "1.0.3179.45", - "Microsoft.WindowsAppSDK.Base": "1.8.251216001", - "Microsoft.WindowsAppSDK.Foundation": "1.8.260505001", - "Microsoft.WindowsAppSDK.InteractiveExperiences": "1.8.260430001" - } - } - } - } -} diff --git a/packages/native/Callout/windows/Callout/pch.cpp b/packages/native/Callout/windows/Callout/pch.cpp deleted file mode 100644 index bcb5590be1..0000000000 --- a/packages/native/Callout/windows/Callout/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" diff --git a/packages/native/Callout/windows/Callout/pch.h b/packages/native/Callout/windows/Callout/pch.h deleted file mode 100644 index 4eac9041ed..0000000000 --- a/packages/native/Callout/windows/Callout/pch.h +++ /dev/null @@ -1,28 +0,0 @@ -// pch.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#pragma once - -#define NOMINMAX 1 -//#define WIN32_LEAN_AND_MEAN 1 -//#define WINRT_LEAN_AND_MEAN 1 - -// Windows Header Files -#include -#undef GetCurrentTime -#include - -// WinRT Header Files -#include -#include -#include - -// C RunTime Header Files -#include -#include -#include -#include - -// Reference additional headers your project requires here diff --git a/packages/native/Callout/windows/ExperimentalFeatures.props b/packages/native/Callout/windows/ExperimentalFeatures.props deleted file mode 100644 index db10c59f9a..0000000000 --- a/packages/native/Callout/windows/ExperimentalFeatures.props +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - true - - - true - - true - - - diff --git a/packages/native/Callout/windows/NuGet.Config b/packages/native/Callout/windows/NuGet.Config deleted file mode 100644 index aee3137fd7..0000000000 --- a/packages/native/Callout/windows/NuGet.Config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/packages/native/Callout/CHANGELOG.json b/packages/shim/callout/CHANGELOG.json similarity index 100% rename from packages/native/Callout/CHANGELOG.json rename to packages/shim/callout/CHANGELOG.json diff --git a/packages/native/Callout/CHANGELOG.md b/packages/shim/callout/CHANGELOG.md similarity index 100% rename from packages/native/Callout/CHANGELOG.md rename to packages/shim/callout/CHANGELOG.md diff --git a/packages/shim/callout/README.md b/packages/shim/callout/README.md new file mode 100644 index 0000000000..af29596036 --- /dev/null +++ b/packages/shim/callout/README.md @@ -0,0 +1,18 @@ +# Callout compatibility shim + +`@fluentui-react-native/callout` preserves its existing named exports by +re-exporting the implementation and public types from +`@fluentui-react-native/native-core`. + +New consumers should import `Callout`, `CalloutProps`, and `CalloutHandle` +from `@fluentui-react-native/native-core`. Deprecated compatibility aliases +remain available through this shim. + +This package owns no native code, codegen specification, pod, or Windows +project. Native applications must list `@fluentui-react-native/native-core` +as a direct dependency so native tooling discovers its pod and Windows +Fabric library. Remove explicit `FRNCallout` pod declarations and regenerate +autolinking/Pods after updating. + +See the [Callout contract](../../agentic/native-core/src/components/callout/SPEC.md) +and [native-core organization](../../agentic/native-core/README.md). diff --git a/packages/native/Callout/jest.config.cjs b/packages/shim/callout/jest.config.cjs similarity index 100% rename from packages/native/Callout/jest.config.cjs rename to packages/shim/callout/jest.config.cjs diff --git a/packages/native/Callout/package.json b/packages/shim/callout/package.json similarity index 77% rename from packages/native/Callout/package.json rename to packages/shim/callout/package.json index 62402c199f..5a99dda312 100644 --- a/packages/native/Callout/package.json +++ b/packages/shim/callout/package.json @@ -1,16 +1,24 @@ { "name": "@fluentui-react-native/callout", "version": "0.29.6", - "description": "A cross-platform Callout component using the Fluent Design System", + "description": "Compatibility shim for the native-core Callout component.", "homepage": "https://github.com/microsoft/fluentui-react-native", "license": "MIT", "author": "", "repository": { "type": "git", "url": "https://github.com/microsoft/fluentui-react-native.git", - "directory": "packages/native/Callout" + "directory": "packages/shim/callout" }, + "files": [ + "lib/index.js", + "lib/index.js.map", + "lib/index.d.ts", + "lib/index.d.ts.map", + "src/index.ts" + ], "type": "module", + "sideEffects": false, "main": "lib/index.js", "module": "lib/index.js", "types": "lib/index.d.ts", @@ -31,7 +39,7 @@ "update-snapshots": "fluentui-scripts jest -u" }, "dependencies": { - "@fluentui-react-native/framework-base": "workspace:*" + "@fluentui-react-native/native-core": "workspace:*" }, "devDependencies": { "@babel/core": "catalog:", @@ -74,32 +82,9 @@ "optional": true } }, - "codegenConfig": { - "name": "FRNCalloutSpec", - "type": "all", - "jsSrcsDir": "src", - "ios": { - "componentProvider": { - "Callout": "RCTCalloutComponentView" - } - }, - "windows": { - "generators": "componentsWindows", - "namespace": "winrt::FluentUI::Callout::Codegen", - "outputDirectory": "windows/Callout/codegen", - "separateDataTypes": true - } - }, "furn": { "jestPlatform": "win32" }, - "react-native-windows": { - "init-windows": { - "name": "Callout", - "namespace": "FluentUI.Callout", - "template": "cpp-lib" - } - }, "rnx-kit": { "kitType": "library", "alignDeps": { diff --git a/packages/shim/callout/src/index.test.ts b/packages/shim/callout/src/index.test.ts new file mode 100644 index 0000000000..134fd33cf1 --- /dev/null +++ b/packages/shim/callout/src/index.test.ts @@ -0,0 +1,22 @@ +import { Callout, calloutName } from './index'; +import { Callout as NativeCallout, calloutName as nativeCalloutName } from '@fluentui-react-native/native-core'; +import type { CalloutHandle, CalloutProps, ICalloutProps, ICalloutTokens } from './index'; +import type { CalloutHandle as NativeCalloutHandle, CalloutProps as NativeCalloutProps } from '@fluentui-react-native/native-core'; + +const props: ICalloutProps & ICalloutTokens = { directionalHint: 'bottomCenter' }; +const nativeProps: NativeCalloutProps = props; +const legacyProps: CalloutProps = nativeProps; +const handle: NativeCalloutHandle = { blurWindow() {}, focusWindow() {} }; +const legacyHandle: CalloutHandle = handle; + +describe('Callout compatibility shim', () => { + it('re-exports the exact native-core component and name', () => { + expect(Callout).toBe(NativeCallout); + expect(calloutName).toBe(nativeCalloutName); + }); + + it('preserves public and deprecated type contracts', () => { + expect(legacyProps).toBe(props); + expect(legacyHandle).toBe(handle); + }); +}); diff --git a/packages/native/Callout/src/index.ts b/packages/shim/callout/src/index.ts similarity index 60% rename from packages/native/Callout/src/index.ts rename to packages/shim/callout/src/index.ts index 9925f9225e..235b9951ed 100644 --- a/packages/native/Callout/src/index.ts +++ b/packages/shim/callout/src/index.ts @@ -1,4 +1,4 @@ -export { calloutName } from './Callout.types'; +export { Callout, calloutName } from '@fluentui-react-native/native-core'; export type { CalloutHandle, CalloutNativeCommands, @@ -9,5 +9,4 @@ export type { ICalloutProps, ICalloutTokens, RestoreFocusEvent, -} from './Callout.types'; -export { Callout } from './Callout'; +} from '@fluentui-react-native/native-core'; diff --git a/packages/native/Callout/tsconfig.json b/packages/shim/callout/tsconfig.json similarity index 87% rename from packages/native/Callout/tsconfig.json rename to packages/shim/callout/tsconfig.json index d13fbc90df..a1c5ec1e71 100644 --- a/packages/native/Callout/tsconfig.json +++ b/packages/shim/callout/tsconfig.json @@ -10,7 +10,7 @@ "exclude": ["**/*.stories.ts", "**/*.stories.tsx"], "references": [ { - "path": "../../framework-base/tsconfig.json" + "path": "../../agentic/native-core/tsconfig.json" }, { "path": "../../../scripts/tsconfig.json" diff --git a/packages/components/FocusZone/CHANGELOG.json b/packages/shim/focus-zone/CHANGELOG.json similarity index 100% rename from packages/components/FocusZone/CHANGELOG.json rename to packages/shim/focus-zone/CHANGELOG.json diff --git a/packages/components/FocusZone/CHANGELOG.md b/packages/shim/focus-zone/CHANGELOG.md similarity index 100% rename from packages/components/FocusZone/CHANGELOG.md rename to packages/shim/focus-zone/CHANGELOG.md diff --git a/packages/shim/focus-zone/README.md b/packages/shim/focus-zone/README.md new file mode 100644 index 0000000000..69f23ccb57 --- /dev/null +++ b/packages/shim/focus-zone/README.md @@ -0,0 +1,19 @@ +# FocusZone compatibility shim + +`@fluentui-react-native/focus-zone` preserves its existing named exports by +re-exporting the implementation and public types from +`@fluentui-react-native/native-core`. Its legacy `NativeProps` export aliases +the component-qualified `FocusZoneNativeProps` export. + +New consumers should import `FocusZone` and `FocusZoneProps` from +`@fluentui-react-native/native-core`. Deprecated compatibility types remain +available through this shim. + +This package owns no native code, codegen specification, pod, or Windows +project. Native applications must list `@fluentui-react-native/native-core` +as a direct dependency so native tooling discovers its pod and Windows +Fabric library. Remove explicit `RCTFocusZone` pod declarations and regenerate +autolinking/Pods after updating. Win32 keeps its host-provided native component. + +See the [FocusZone contract](../../agentic/native-core/src/components/focus-zone/SPEC.md) +and [native-core organization](../../agentic/native-core/README.md). diff --git a/packages/shim/focus-zone/jest.config.cjs b/packages/shim/focus-zone/jest.config.cjs new file mode 100644 index 0000000000..b391f5b662 --- /dev/null +++ b/packages/shim/focus-zone/jest.config.cjs @@ -0,0 +1 @@ +module.exports = require('@fluentui-react-native/scripts/jest-config'); diff --git a/packages/components/FocusZone/package.json b/packages/shim/focus-zone/package.json similarity index 74% rename from packages/components/FocusZone/package.json rename to packages/shim/focus-zone/package.json index 1adc1d604c..bb222eb5c9 100644 --- a/packages/components/FocusZone/package.json +++ b/packages/shim/focus-zone/package.json @@ -1,15 +1,23 @@ { "name": "@fluentui-react-native/focus-zone", "version": "0.23.6", - "description": "A cross-platform FocusZone component using the Fluent Design System", + "description": "Compatibility shim for the native-core FocusZone component.", "license": "MIT", "author": "", "repository": { "type": "git", "url": "https://github.com/microsoft/fluentui-react-native.git", - "directory": "packages/components/FocusZone" + "directory": "packages/shim/focus-zone" }, + "files": [ + "lib/index.js", + "lib/index.js.map", + "lib/index.d.ts", + "lib/index.d.ts.map", + "src/index.ts" + ], "type": "module", + "sideEffects": false, "main": "lib/index.js", "module": "lib/index.js", "types": "lib/index.d.ts", @@ -30,12 +38,10 @@ "update-snapshots": "fluentui-scripts jest -u" }, "dependencies": { - "@fluentui-react-native/framework-base": "workspace:*", - "@fluentui-react-native/interactive-hooks": "workspace:*" + "@fluentui-react-native/native-core": "workspace:*" }, "devDependencies": { "@babel/core": "catalog:", - "@fluentui-react-native/desktop-driver": "workspace:*", "@fluentui-react-native/scripts": "workspace:*", "@office-iss/react-native-win32": "^0.81.8", "@react-native-community/cli": "^20.0.0", @@ -75,32 +81,9 @@ "optional": true } }, - "codegenConfig": { - "name": "FRNFocusZoneSpec", - "type": "all", - "jsSrcsDir": "src", - "ios": { - "componentProvider": { - "FocusZone": "RCTFocusZoneComponentView" - } - }, - "windows": { - "generators": "componentsWindows", - "namespace": "FRNFocusZoneCodegen", - "outputDirectory": "windows/FRNFocusZone/codegen", - "separateDataTypes": true - } - }, "furn": { "jestPlatform": "win32" }, - "react-native-windows": { - "init-windows": { - "name": "FRNFocusZone", - "namespace": "FRNFocusZone", - "template": "cpp-lib" - } - }, "rnx-kit": { "kitType": "library", "alignDeps": { diff --git a/packages/shim/focus-zone/src/index.test.ts b/packages/shim/focus-zone/src/index.test.ts new file mode 100644 index 0000000000..f7de321dbe --- /dev/null +++ b/packages/shim/focus-zone/src/index.test.ts @@ -0,0 +1,26 @@ +import { FocusZone, focusZoneName } from './index'; +import { FocusZone as NativeFocusZone, focusZoneName as nativeFocusZoneName } from '@fluentui-react-native/native-core'; +import type { FocusZoneProps, FocusZoneRenderData, FocusZoneState, FocusZoneTokens, FocusZoneType, NativeProps } from './index'; +import type { FocusZoneNativeProps, FocusZoneProps as NativeFocusZoneProps } from '@fluentui-react-native/native-core'; + +const props: FocusZoneProps = { focusZoneDirection: 'horizontal', isCircularNavigation: true }; +const nativeProps: NativeFocusZoneProps = props; +const nativeRootProps: FocusZoneNativeProps = { navigateAtEnd: 'NavigateWrap' }; +const legacyRootProps: NativeProps = nativeRootProps; +const state: FocusZoneState = {}; +const tokens: FocusZoneTokens = {}; +const renderData: FocusZoneRenderData = { slotProps: { root: legacyRootProps }, state }; +const legacyType: FocusZoneType = { props, tokens, slotProps: renderData.slotProps, state }; + +describe('FocusZone compatibility shim', () => { + it('re-exports the exact native-core component and name', () => { + expect(FocusZone).toBe(NativeFocusZone); + expect(focusZoneName).toBe(nativeFocusZoneName); + }); + + it('preserves public, native, and deprecated type contracts', () => { + expect(nativeProps).toBe(props); + expect(legacyRootProps).toBe(nativeRootProps); + expect(legacyType.state).toBe(state); + }); +}); diff --git a/packages/components/FocusZone/src/index.ts b/packages/shim/focus-zone/src/index.ts similarity index 54% rename from packages/components/FocusZone/src/index.ts rename to packages/shim/focus-zone/src/index.ts index f84aa91575..3c25d28d3d 100644 --- a/packages/components/FocusZone/src/index.ts +++ b/packages/shim/focus-zone/src/index.ts @@ -1,4 +1,4 @@ -export { focusZoneName } from './FocusZone.types'; +export { FocusZone, focusZoneName } from '@fluentui-react-native/native-core'; export type { FocusZoneDirection, FocusZoneProps, @@ -8,7 +8,6 @@ export type { FocusZoneTabNavigation, FocusZoneTokens, FocusZoneType, - NativeProps, + FocusZoneNativeProps as NativeProps, NavigateAtEnd, -} from './FocusZone.types'; -export { FocusZone } from './FocusZone'; +} from '@fluentui-react-native/native-core'; diff --git a/packages/components/FocusZone/tsconfig.json b/packages/shim/focus-zone/tsconfig.json similarity index 65% rename from packages/components/FocusZone/tsconfig.json rename to packages/shim/focus-zone/tsconfig.json index 243ef12c40..a1c5ec1e71 100644 --- a/packages/components/FocusZone/tsconfig.json +++ b/packages/shim/focus-zone/tsconfig.json @@ -10,13 +10,7 @@ "exclude": ["**/*.stories.ts", "**/*.stories.tsx"], "references": [ { - "path": "../../agentic/desktop-driver/tsconfig.json" - }, - { - "path": "../../framework-base/tsconfig.json" - }, - { - "path": "../../utils/interactive-hooks/tsconfig.json" + "path": "../../agentic/native-core/tsconfig.json" }, { "path": "../../../scripts/tsconfig.json" diff --git a/tsconfig.json b/tsconfig.json index 2d951847c5..1bb404723f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -64,9 +64,6 @@ { "path": "packages/components/FocusTrapZone/tsconfig.json" }, - { - "path": "packages/components/FocusZone/tsconfig.json" - }, { "path": "packages/components/Icon/tsconfig.json" }, @@ -209,7 +206,10 @@ "path": "packages/libraries/core/tsconfig.json" }, { - "path": "packages/native/Callout/tsconfig.json" + "path": "packages/shim/callout/tsconfig.json" + }, + { + "path": "packages/shim/focus-zone/tsconfig.json" }, { "path": "packages/shim/theme/tsconfig.json" diff --git a/yarn.lock b/yarn.lock index 9357564ab9..3273416e4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2565,10 +2565,9 @@ __metadata: resolution: "@fluentui-react-native/agentic-components-storybook@workspace:apps/storybook" dependencies: "@babel/core": "catalog:" - "@fluentui-react-native/callout": "workspace:*" "@fluentui-react-native/components": "workspace:*" "@fluentui-react-native/desktop-driver": "workspace:*" - "@fluentui-react-native/focus-zone": "workspace:*" + "@fluentui-react-native/native-core": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" "@fluentui-react-native/storybook-desktop": "workspace:*" "@fluentui-react-native/storybook-desktop-runtime": "workspace:*" @@ -2852,12 +2851,12 @@ __metadata: languageName: unknown linkType: soft -"@fluentui-react-native/callout@workspace:*, @fluentui-react-native/callout@workspace:packages/native/Callout": +"@fluentui-react-native/callout@workspace:*, @fluentui-react-native/callout@workspace:packages/shim/callout": version: 0.0.0-use.local - resolution: "@fluentui-react-native/callout@workspace:packages/native/Callout" + resolution: "@fluentui-react-native/callout@workspace:packages/shim/callout" dependencies: "@babel/core": "catalog:" - "@fluentui-react-native/framework-base": "workspace:*" + "@fluentui-react-native/native-core": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" "@office-iss/react-native-win32": "npm:^0.81.8" "@react-native-community/cli": "npm:^20.0.0" @@ -3017,11 +3016,10 @@ __metadata: resolution: "@fluentui-react-native/components@workspace:packages/agentic/components" dependencies: "@babel/core": "catalog:" - "@fluentui-react-native/callout": "workspace:*" "@fluentui-react-native/design": "workspace:*" "@fluentui-react-native/desktop-driver": "workspace:*" - "@fluentui-react-native/focus-zone": "workspace:*" "@fluentui-react-native/framework-base": "workspace:*" + "@fluentui-react-native/native-core": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" "@fluentui-react-native/storybook-desktop": "workspace:*" "@office-iss/react-native-win32": "npm:^0.81.0" @@ -4112,14 +4110,12 @@ __metadata: languageName: unknown linkType: soft -"@fluentui-react-native/focus-zone@workspace:*, @fluentui-react-native/focus-zone@workspace:packages/components/FocusZone": +"@fluentui-react-native/focus-zone@workspace:*, @fluentui-react-native/focus-zone@workspace:packages/shim/focus-zone": version: 0.0.0-use.local - resolution: "@fluentui-react-native/focus-zone@workspace:packages/components/FocusZone" + resolution: "@fluentui-react-native/focus-zone@workspace:packages/shim/focus-zone" dependencies: "@babel/core": "catalog:" - "@fluentui-react-native/desktop-driver": "workspace:*" - "@fluentui-react-native/framework-base": "workspace:*" - "@fluentui-react-native/interactive-hooks": "workspace:*" + "@fluentui-react-native/native-core": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" "@office-iss/react-native-win32": "npm:^0.81.8" "@react-native-community/cli": "npm:^20.0.0" @@ -4515,11 +4511,48 @@ __metadata: languageName: unknown linkType: soft -"@fluentui-react-native/native-core@workspace:packages/agentic/native-core": +"@fluentui-react-native/native-core@workspace:*, @fluentui-react-native/native-core@workspace:packages/agentic/native-core": version: 0.0.0-use.local resolution: "@fluentui-react-native/native-core@workspace:packages/agentic/native-core" dependencies: + "@babel/core": "catalog:" + "@fluentui-react-native/framework-base": "workspace:*" + "@fluentui-react-native/interactive-hooks": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" + "@office-iss/react-native-win32": "npm:^0.81.8" + "@react-native-community/cli": "npm:^20.0.0" + "@react-native-community/cli-platform-android": "npm:^20.0.0" + "@react-native-community/cli-platform-ios": "npm:^20.0.0" + "@react-native-windows/cli": "npm:^0.81.0" + "@react-native/babel-preset": "npm:^0.81.0" + "@react-native/metro-config": "npm:^0.81.0" + "@storybook/react-native": "npm:^10.4.7" + "@testing-library/react-native": "catalog:" + "@types/react": "npm:~19.1.4" + "@types/react-test-renderer": "npm:^19.1.0" + react: "npm:19.1.4" + react-native: "npm:^0.81.6" + react-native-macos: "npm:^0.81.0" + react-native-windows: "npm:^0.81.0" + react-test-renderer: "npm:19.1.4" + storybook: "npm:^10.4.0" + test-renderer: "catalog:" + peerDependencies: + "@office-iss/react-native-win32": ^0.74.0 || ^0.81.0 + "@types/react": ~18.2.0 || ~19.0.0 || ~19.1.4 + react: 18.2.0 || 19.0.0 || 19.1.4 + react-native: ^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.6 + react-native-macos: ^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.0 + react-native-windows: ^0.73.0 || ^0.74.0 || ^0.78.0 || ^0.81.0 + peerDependenciesMeta: + "@office-iss/react-native-win32": + optional: true + "@types/react": + optional: true + react-native-macos: + optional: true + react-native-windows: + optional: true languageName: unknown linkType: soft @@ -5060,9 +5093,9 @@ __metadata: resolution: "@fluentui-react-native/storybook-desktop-runtime@workspace:packages/agentic/storybook-desktop-runtime" dependencies: "@babel/core": "catalog:" - "@fluentui-react-native/callout": "workspace:*" "@fluentui-react-native/default-theme": "workspace:*" "@fluentui-react-native/design": "workspace:*" + "@fluentui-react-native/native-core": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" "@office-iss/react-native-win32": "npm:^0.81.0" "@react-native-community/cli": "npm:^20.0.0" @@ -5485,6 +5518,7 @@ __metadata: "@fluentui-react-native/experimental-shimmer": "workspace:*" "@fluentui-react-native/focus-zone": "workspace:*" "@fluentui-react-native/menu-button": "workspace:*" + "@fluentui-react-native/native-core": "workspace:*" "@fluentui-react-native/radio-group": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" "@fluentui-react-native/tester-core": "workspace:*" From 666bcdd6701e03b9463b0b15d703f3526b6e9264 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Fri, 2 Oct 2026 13:37:09 -0700 Subject: [PATCH 3/9] move existing implementations to legacy submodule --- .changeset/native-core-legacy-entrypoint.md | 9 +++ AGENTS.md | 3 +- apps/storybook/README.md | 3 +- .../agentic/components/WIN32-FOCUS-PLAN.md | 2 +- .../focus-zone/focus-zone.stories.tsx | 4 +- packages/agentic/native-core/README.md | 55 ++++++++++++------- packages/agentic/native-core/package.json | 6 ++ packages/agentic/native-core/src/index.ts | 28 +--------- .../callout/Callout.stories.tsx | 0 .../callout/Callout.tsx | 0 .../callout/Callout.types.ts | 0 .../{components => legacy}/callout/SPEC.md | 5 +- .../callout/__tests__/Callout.test.tsx | 0 .../callout/__tests__/Callout.types.test.ts | 0 .../__snapshots__/Callout.test.tsx.snap | 0 .../focus-zone/FocusZone.tsx | 0 .../focus-zone/FocusZone.types.ts | 0 .../{components => legacy}/focus-zone/SPEC.md | 3 + .../focus-zone/__tests__/FocusZone.test.tsx | 0 .../__tests__/FocusZone.types.test.ts | 0 .../agentic/native-core/src/legacy/index.ts | 27 +++++++++ .../src/legacy/legacy.types.test.ts | 30 ++++++++++ .../native-core/tests/entrypoints.test.mjs | 11 +++- .../tests/native-ownership.test.mjs | 2 +- .../src/Win32CalloutPortal.tsx | 4 +- .../deprecated/foundation-compose/AGENTS.md | 2 +- packages/shim/callout/README.md | 6 +- packages/shim/callout/src/index.test.ts | 4 +- packages/shim/callout/src/index.ts | 4 +- packages/shim/focus-zone/README.md | 6 +- packages/shim/focus-zone/src/index.test.ts | 4 +- packages/shim/focus-zone/src/index.ts | 4 +- 32 files changed, 151 insertions(+), 71 deletions(-) create mode 100644 .changeset/native-core-legacy-entrypoint.md rename packages/agentic/native-core/src/{components => legacy}/callout/Callout.stories.tsx (100%) rename packages/agentic/native-core/src/{components => legacy}/callout/Callout.tsx (100%) rename packages/agentic/native-core/src/{components => legacy}/callout/Callout.types.ts (100%) rename packages/agentic/native-core/src/{components => legacy}/callout/SPEC.md (91%) rename packages/agentic/native-core/src/{components => legacy}/callout/__tests__/Callout.test.tsx (100%) rename packages/agentic/native-core/src/{components => legacy}/callout/__tests__/Callout.types.test.ts (100%) rename packages/agentic/native-core/src/{components => legacy}/callout/__tests__/__snapshots__/Callout.test.tsx.snap (100%) rename packages/agentic/native-core/src/{components => legacy}/focus-zone/FocusZone.tsx (100%) rename packages/agentic/native-core/src/{components => legacy}/focus-zone/FocusZone.types.ts (100%) rename packages/agentic/native-core/src/{components => legacy}/focus-zone/SPEC.md (95%) rename packages/agentic/native-core/src/{components => legacy}/focus-zone/__tests__/FocusZone.test.tsx (100%) rename packages/agentic/native-core/src/{components => legacy}/focus-zone/__tests__/FocusZone.types.test.ts (100%) create mode 100644 packages/agentic/native-core/src/legacy/index.ts create mode 100644 packages/agentic/native-core/src/legacy/legacy.types.test.ts diff --git a/.changeset/native-core-legacy-entrypoint.md b/.changeset/native-core-legacy-entrypoint.md new file mode 100644 index 0000000000..72f599131c --- /dev/null +++ b/.changeset/native-core-legacy-entrypoint.md @@ -0,0 +1,9 @@ +--- +"@fluentui-react-native/native-core": minor +"@fluentui-react-native/callout": patch +"@fluentui-react-native/focus-zone": patch +"@fluentui-react-native/components": patch +"@fluentui-react-native/storybook-desktop-runtime": patch +--- + +Isolate the existing Callout and FocusZone JavaScript wrappers under native-core/legacy instead of the package root. Compatibility shims retain their exports through this submodule; native implementations and codegen specifications are unchanged. diff --git a/AGENTS.md b/AGENTS.md index f05cbd3606..fe2e40686a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -275,7 +275,8 @@ The composition framework uses precise types for better type safety: - **Do not use barrel exports (`export * from '...'`)** - wildcard re-exports break tree-shaking because bundlers cannot statically determine which symbols are used, so unused code is retained in consumers' bundles. Always use explicit named re-exports instead, e.g. `export { Foo, Bar } from './module'` and `export type { Baz } from './module'`. **Native Modules**: Callout and FocusZone implementations live in `@fluentui-react-native/native-core`; their former packages -are JS-only compatibility shims under `/packages/shim/`. Components with native code: +are JS-only compatibility shims under `/packages/shim/`. Their JavaScript wrappers live in `src/legacy` and are exported +only from `@fluentui-react-native/native-core/legacy`, not native-core's root entrypoint. Components with native code: - Typically have one root slot wrapping the native component - Use `codegenNativeComponent` for new architecture compatibility diff --git a/apps/storybook/README.md b/apps/storybook/README.md index ee18b76a50..b34184c251 100644 --- a/apps/storybook/README.md +++ b/apps/storybook/README.md @@ -3,7 +3,8 @@ On-device [Storybook](https://storybook.js.org/) test app (Storybook for React Native v10) for `@fluentui-react-native/components` and `@fluentui-react-native/native-core`. It loads every `*.stories.(ts|tsx)` file from the agentic components package plus native-core's Callout -stories. FocusZone stories remain agentic primitive stories and use native-core's implementation. +stories. FocusZone stories remain agentic primitive stories and use native-core's +`legacy` entrypoint, as does the Win32 Callout portal. Native-core owns the shared macOS pod and Windows Fabric library for both components. The reusable desktop CLI and configuration live in diff --git a/packages/agentic/components/WIN32-FOCUS-PLAN.md b/packages/agentic/components/WIN32-FOCUS-PLAN.md index c857fe0509..93dc5b7123 100644 --- a/packages/agentic/components/WIN32-FOCUS-PLAN.md +++ b/packages/agentic/components/WIN32-FOCUS-PLAN.md @@ -246,7 +246,7 @@ paths. The installed Win32 JavaScript inspected was `packages\components\Menu\src\MenuPopover\useMenuPopover.ts:42-84,100-118`. - **V6:** `packages\components\TabList\src\Tab\useTab.win32.ts:64-110,142-152`; `packages\components\TabList\src\TabList\useTabList.ts:142-176,183-200`. -- **V7:** `packages\agentic\native-core\src\components\focus-zone\FocusZone.tsx:15-45`; +- **V7:** `packages\agentic\native-core\src\legacy\focus-zone\FocusZone.tsx:15-45`; `packages\components\FocusTrapZone\src\FocusTrapZone.ts:15-31`. These wrappers do not by themselves prove native trap/restoration semantics. - **V8:** `apps\tester-core\src\TestComponents\FocusZone\FocusZoneE2ETest.tsx:1-120`; diff --git a/packages/agentic/components/src/primitives/focus-zone/focus-zone.stories.tsx b/packages/agentic/components/src/primitives/focus-zone/focus-zone.stories.tsx index 2463df8fe9..15af215ab2 100644 --- a/packages/agentic/components/src/primitives/focus-zone/focus-zone.stories.tsx +++ b/packages/agentic/components/src/primitives/focus-zone/focus-zone.stories.tsx @@ -4,8 +4,8 @@ import { Platform, Pressable, StyleSheet, Text, View } from 'react-native'; import type { StyleProp, ViewStyle } from 'react-native'; import type { WdioStory } from '@fluentui-react-native/storybook-desktop/testing'; -import { FocusZone } from '@fluentui-react-native/native-core'; -import type { FocusZoneDirection, FocusZoneProps, FocusZoneTabNavigation } from '@fluentui-react-native/native-core'; +import { FocusZone } from '@fluentui-react-native/native-core/legacy'; +import type { FocusZoneDirection, FocusZoneProps, FocusZoneTabNavigation } from '@fluentui-react-native/native-core/legacy'; import type { Meta, StoryObj } from '@storybook/react-native'; import { Button } from '../../components/button/button'; diff --git a/packages/agentic/native-core/README.md b/packages/agentic/native-core/README.md index d1dba69b34..6c741271fb 100644 --- a/packages/agentic/native-core/README.md +++ b/packages/agentic/native-core/README.md @@ -9,16 +9,27 @@ dependency. ## Current contents -The root exports the existing unstyled `Callout` and `FocusZone` wrappers and -their public types. Their JS, tests, specifications, macOS implementations, -and Windows Fabric implementations live here. The `macos`, `windows`, `win`, -and `win32` entrypoints are reserved for future host-specific APIs and remain -empty; shared components belong to the root. +The existing unstyled `Callout` and `FocusZone` JavaScript wrappers, public +types, tests, stories, and specifications live under `src/legacy` and are +exported only from `@fluentui-react-native/native-core/legacy`. They are not +exported by the root entrypoint, which is reserved for modern cross-platform +APIs. The `macos`, `windows`, `win`, and `win32` entrypoints are reserved for +future host-specific APIs and remain empty. `@fluentui-react-native/callout` and `@fluentui-react-native/focus-zone` are JS-only compatibility shims under `packages/shim`. Deprecated type aliases and -native registration names are preserved. The root uses the component-qualified -`FocusZoneNativeProps`; the FocusZone shim retains its old `NativeProps` name. +native registration names are preserved. The legacy entrypoint uses the +component-qualified `FocusZoneNativeProps`; the FocusZone shim retains its old +`NativeProps` name. + +```ts +import { Callout, FocusZone } from '@fluentui-react-native/native-core/legacy'; +import type { CalloutProps, FocusZoneProps } from '@fluentui-react-native/native-core/legacy'; +``` + +Native implementations and shared codegen inputs remain outside `src/legacy`; +the legacy boundary applies to the JavaScript component API, not native +ownership or renderer support. Native applications must directly depend on this package for autolinking. macOS now uses one `FRNNativeCore` pod, and Windows uses one @@ -29,16 +40,17 @@ to use its host-provided `RCTCallout` and `RCTFocusZone`. ## Public entrypoints -| Import | Scope | Source entrypoint | -| -------------------------------------------- | --------------------------------------- | ---------------------- | -| `@fluentui-react-native/native-core` | Cross-platform APIs | `src/index.ts` | -| `@fluentui-react-native/native-core/macos` | macOS-specific APIs | `src/macos/index.ts` | -| `@fluentui-react-native/native-core/windows` | React Native Windows-specific APIs | `src/windows/index.ts` | -| `@fluentui-react-native/native-core/win` | APIs shared by Windows and Win32 | `src/win/index.ts` | -| `@fluentui-react-native/native-core/win32` | Office React Native Win32-specific APIs | `src/win32/index.ts` | +| Import | Scope | Source entrypoint | +| -------------------------------------------- | ------------------------------------------- | ---------------------- | +| `@fluentui-react-native/native-core` | Cross-platform APIs | `src/index.ts` | +| `@fluentui-react-native/native-core/legacy` | Legacy Callout/FocusZone wrappers and types | `src/legacy/index.ts` | +| `@fluentui-react-native/native-core/macos` | macOS-specific APIs | `src/macos/index.ts` | +| `@fluentui-react-native/native-core/windows` | React Native Windows-specific APIs | `src/windows/index.ts` | +| `@fluentui-react-native/native-core/win` | APIs shared by Windows and Win32 | `src/win/index.ts` | +| `@fluentui-react-native/native-core/win32` | Office React Native Win32-specific APIs | `src/win32/index.ts` | Each entrypoint explicitly exports components, module facades, and their -public types. The root is not an umbrella export of the platform submodules; +public types. The root does not re-export legacy or platform submodules; `win` is not an umbrella export of `windows` and `win32`. Give an API its narrowest applicable public home. Shared implementation does not itself make an API cross-platform. @@ -57,9 +69,9 @@ imports. Never use wildcard re-exports. ## JavaScript and specification layout -Component wrappers live in `src/components/callout` and -`src/components/focus-zone`, with codegen inputs in `src/specs/components`. -Add module and internal utility directories as real features arrive: +Legacy wrappers live in `src/legacy/callout` and `src/legacy/focus-zone`, with +shared codegen inputs in `src/specs/components`. Reserve `src/components` for +modern wrappers and add module/internal directories as real features arrive: ```text src/ @@ -68,6 +80,10 @@ src/ windows/index.ts win/index.ts win32/index.ts + legacy/ + index.ts Explicit legacy-only public exports + callout/ Existing Callout wrapper, types, tests, and stories + focus-zone/ Existing FocusZone wrapper, types, and tests components/ / .tsx Thin public wrapper, when shared @@ -209,10 +225,11 @@ yarn workspace @fluentui-react-native/native-core codegen:windows --check yarn build ``` -Node tests validate the five export maps, emitted JS/declaration files, +Node tests validate the six export maps, emitted JS/declaration files, conditional resolution, shared native ownership, and JS-only shims. Colocated Jest tests retain wrapper behavior, imperative refs/commands, slot acceptance, and public type contracts; shim tests verify exact component identity. +Runtime and compile-time checks reject legacy component/type exports at root. The package prebuild regenerates Windows bindings from the shared spec. Native verification must cover registration, events/commands, refs, unmount cleanup, diff --git a/packages/agentic/native-core/package.json b/packages/agentic/native-core/package.json index 408487ba1f..b31e76f650 100644 --- a/packages/agentic/native-core/package.json +++ b/packages/agentic/native-core/package.json @@ -41,6 +41,12 @@ "import": "./lib/index.js", "default": "./src/index.ts" }, + "./legacy": { + "types": "./lib/legacy/index.d.ts", + "react-native": "./src/legacy/index.ts", + "import": "./lib/legacy/index.js", + "default": "./src/legacy/index.ts" + }, "./macos": { "types": "./lib/macos/index.d.ts", "react-native": "./src/macos/index.ts", diff --git a/packages/agentic/native-core/src/index.ts b/packages/agentic/native-core/src/index.ts index a576fa7f8d..cb0ff5c3b5 100644 --- a/packages/agentic/native-core/src/index.ts +++ b/packages/agentic/native-core/src/index.ts @@ -1,27 +1 @@ -export { Callout } from './components/callout/Callout'; -export { calloutName } from './components/callout/Callout.types'; -export type { - CalloutHandle, - CalloutNativeCommands, - CalloutProps, - CalloutTokens, - DirectionalHint, - DismissBehaviors, - ICalloutProps, - ICalloutTokens, - RestoreFocusEvent, -} from './components/callout/Callout.types'; -export { FocusZone } from './components/focus-zone/FocusZone'; -export { focusZoneName } from './components/focus-zone/FocusZone.types'; -export type { - FocusZoneDirection, - FocusZoneProps, - FocusZoneRenderData, - FocusZoneSlotProps, - FocusZoneState, - FocusZoneTabNavigation, - FocusZoneTokens, - FocusZoneType, - NativeProps as FocusZoneNativeProps, - NavigateAtEnd, -} from './components/focus-zone/FocusZone.types'; +export {}; diff --git a/packages/agentic/native-core/src/components/callout/Callout.stories.tsx b/packages/agentic/native-core/src/legacy/callout/Callout.stories.tsx similarity index 100% rename from packages/agentic/native-core/src/components/callout/Callout.stories.tsx rename to packages/agentic/native-core/src/legacy/callout/Callout.stories.tsx diff --git a/packages/agentic/native-core/src/components/callout/Callout.tsx b/packages/agentic/native-core/src/legacy/callout/Callout.tsx similarity index 100% rename from packages/agentic/native-core/src/components/callout/Callout.tsx rename to packages/agentic/native-core/src/legacy/callout/Callout.tsx diff --git a/packages/agentic/native-core/src/components/callout/Callout.types.ts b/packages/agentic/native-core/src/legacy/callout/Callout.types.ts similarity index 100% rename from packages/agentic/native-core/src/components/callout/Callout.types.ts rename to packages/agentic/native-core/src/legacy/callout/Callout.types.ts diff --git a/packages/agentic/native-core/src/components/callout/SPEC.md b/packages/agentic/native-core/src/legacy/callout/SPEC.md similarity index 91% rename from packages/agentic/native-core/src/components/callout/SPEC.md rename to packages/agentic/native-core/src/legacy/callout/SPEC.md index e57ccfeac8..1e48b41be8 100644 --- a/packages/agentic/native-core/src/components/callout/SPEC.md +++ b/packages/agentic/native-core/src/legacy/callout/SPEC.md @@ -7,6 +7,9 @@ positioned relative to a target ref, registered native anchor, or anchor rectangle. Its JS wrapper, native adapters, and codegen schema are owned by `@fluentui-react-native/native-core`; the old Callout package is a JS-only shim. +The JavaScript component and its public types are exported only from +`@fluentui-react-native/native-core/legacy`, not the package root. + ## Contract - `CalloutProps` extends React Native `ViewProps` with target resolution, @@ -37,7 +40,7 @@ rectangle. Its JS wrapper, native adapters, and codegen schema are owned by ## Compatibility `ICalloutProps`, `ICalloutTokens`, and `CalloutNativeCommands` remain as -deprecated aliases for the modern public types. +deprecated aliases for the public types in the legacy entrypoint. Existing `Callout`/`RCTCallout` native registration names remain unchanged. Native applications must directly depend on `@fluentui-react-native/native-core` diff --git a/packages/agentic/native-core/src/components/callout/__tests__/Callout.test.tsx b/packages/agentic/native-core/src/legacy/callout/__tests__/Callout.test.tsx similarity index 100% rename from packages/agentic/native-core/src/components/callout/__tests__/Callout.test.tsx rename to packages/agentic/native-core/src/legacy/callout/__tests__/Callout.test.tsx diff --git a/packages/agentic/native-core/src/components/callout/__tests__/Callout.types.test.ts b/packages/agentic/native-core/src/legacy/callout/__tests__/Callout.types.test.ts similarity index 100% rename from packages/agentic/native-core/src/components/callout/__tests__/Callout.types.test.ts rename to packages/agentic/native-core/src/legacy/callout/__tests__/Callout.types.test.ts diff --git a/packages/agentic/native-core/src/components/callout/__tests__/__snapshots__/Callout.test.tsx.snap b/packages/agentic/native-core/src/legacy/callout/__tests__/__snapshots__/Callout.test.tsx.snap similarity index 100% rename from packages/agentic/native-core/src/components/callout/__tests__/__snapshots__/Callout.test.tsx.snap rename to packages/agentic/native-core/src/legacy/callout/__tests__/__snapshots__/Callout.test.tsx.snap diff --git a/packages/agentic/native-core/src/components/focus-zone/FocusZone.tsx b/packages/agentic/native-core/src/legacy/focus-zone/FocusZone.tsx similarity index 100% rename from packages/agentic/native-core/src/components/focus-zone/FocusZone.tsx rename to packages/agentic/native-core/src/legacy/focus-zone/FocusZone.tsx diff --git a/packages/agentic/native-core/src/components/focus-zone/FocusZone.types.ts b/packages/agentic/native-core/src/legacy/focus-zone/FocusZone.types.ts similarity index 100% rename from packages/agentic/native-core/src/components/focus-zone/FocusZone.types.ts rename to packages/agentic/native-core/src/legacy/focus-zone/FocusZone.types.ts diff --git a/packages/agentic/native-core/src/components/focus-zone/SPEC.md b/packages/agentic/native-core/src/legacy/focus-zone/SPEC.md similarity index 95% rename from packages/agentic/native-core/src/components/focus-zone/SPEC.md rename to packages/agentic/native-core/src/legacy/focus-zone/SPEC.md index 5379e8851c..2abe612905 100644 --- a/packages/agentic/native-core/src/components/focus-zone/SPEC.md +++ b/packages/agentic/native-core/src/legacy/focus-zone/SPEC.md @@ -7,6 +7,9 @@ keyboard navigation among focusable descendants. Its JS wrapper, native adapters, and codegen schema are owned by `@fluentui-react-native/native-core`; the old FocusZone package is a JS-only shim. +The JavaScript component and its public types are exported only from +`@fluentui-react-native/native-core/legacy`, not the package root. + ## Contract - `FocusZoneProps` extends React Native `ViewProps` with directional, circular, diff --git a/packages/agentic/native-core/src/components/focus-zone/__tests__/FocusZone.test.tsx b/packages/agentic/native-core/src/legacy/focus-zone/__tests__/FocusZone.test.tsx similarity index 100% rename from packages/agentic/native-core/src/components/focus-zone/__tests__/FocusZone.test.tsx rename to packages/agentic/native-core/src/legacy/focus-zone/__tests__/FocusZone.test.tsx diff --git a/packages/agentic/native-core/src/components/focus-zone/__tests__/FocusZone.types.test.ts b/packages/agentic/native-core/src/legacy/focus-zone/__tests__/FocusZone.types.test.ts similarity index 100% rename from packages/agentic/native-core/src/components/focus-zone/__tests__/FocusZone.types.test.ts rename to packages/agentic/native-core/src/legacy/focus-zone/__tests__/FocusZone.types.test.ts diff --git a/packages/agentic/native-core/src/legacy/index.ts b/packages/agentic/native-core/src/legacy/index.ts new file mode 100644 index 0000000000..5ac07b734e --- /dev/null +++ b/packages/agentic/native-core/src/legacy/index.ts @@ -0,0 +1,27 @@ +export { Callout } from './callout/Callout'; +export { calloutName } from './callout/Callout.types'; +export type { + CalloutHandle, + CalloutNativeCommands, + CalloutProps, + CalloutTokens, + DirectionalHint, + DismissBehaviors, + ICalloutProps, + ICalloutTokens, + RestoreFocusEvent, +} from './callout/Callout.types'; +export { FocusZone } from './focus-zone/FocusZone'; +export { focusZoneName } from './focus-zone/FocusZone.types'; +export type { + FocusZoneDirection, + FocusZoneProps, + FocusZoneRenderData, + FocusZoneSlotProps, + FocusZoneState, + FocusZoneTabNavigation, + FocusZoneTokens, + FocusZoneType, + NativeProps as FocusZoneNativeProps, + NavigateAtEnd, +} from './focus-zone/FocusZone.types'; diff --git a/packages/agentic/native-core/src/legacy/legacy.types.test.ts b/packages/agentic/native-core/src/legacy/legacy.types.test.ts new file mode 100644 index 0000000000..2e516f8977 --- /dev/null +++ b/packages/agentic/native-core/src/legacy/legacy.types.test.ts @@ -0,0 +1,30 @@ +import * as root from '../index'; +import { Callout, FocusZone } from './index'; +import type { CalloutProps, FocusZoneProps } from './index'; + +const calloutProps: CalloutProps = { target: 'anchor' }; +const focusZoneProps: FocusZoneProps = { focusZoneDirection: 'horizontal' }; + +// @ts-expect-error CalloutProps is available only from the legacy entrypoint. +const rootCalloutProps: root.CalloutProps = calloutProps; +// @ts-expect-error FocusZoneProps is available only from the legacy entrypoint. +const rootFocusZoneProps: root.FocusZoneProps = focusZoneProps; + +// @ts-expect-error Callout is not exported from the root entrypoint. +const rootCalloutKey: keyof typeof root = 'Callout'; +// @ts-expect-error FocusZone is not exported from the root entrypoint. +const rootFocusZoneKey: keyof typeof root = 'FocusZone'; + +describe('Legacy entrypoint types', () => { + it('exposes the wrappers only from legacy', () => { + expect(Callout).toBeDefined(); + expect(FocusZone).toBeDefined(); + expect(root).not.toHaveProperty(rootCalloutKey); + expect(root).not.toHaveProperty(rootFocusZoneKey); + }); + + it('retains the legacy public prop contracts', () => { + expect(rootCalloutProps).toBe(calloutProps); + expect(rootFocusZoneProps).toBe(focusZoneProps); + }); +}); diff --git a/packages/agentic/native-core/tests/entrypoints.test.mjs b/packages/agentic/native-core/tests/entrypoints.test.mjs index 3fda319b2f..6eb5357d99 100644 --- a/packages/agentic/native-core/tests/entrypoints.test.mjs +++ b/packages/agentic/native-core/tests/entrypoints.test.mjs @@ -8,13 +8,14 @@ const packageRoot = new URL('../', import.meta.url); const manifest = JSON.parse(await readFile(new URL('package.json', packageRoot), 'utf8')); const entrypoints = [ ['.', 'index'], + ['./legacy', 'legacy/index'], ['./macos', 'macos/index'], ['./windows', 'windows/index'], ['./win', 'win/index'], ['./win32', 'win32/index'], ]; -test('declares exactly the five public entrypoints and package metadata', () => { +test('declares exactly the six public entrypoints and package metadata', () => { assert.equal(manifest.name, '@fluentui-react-native/native-core'); assert.deepEqual(Object.keys(manifest.exports), [...entrypoints.map(([entrypoint]) => entrypoint), './package.json']); assert.equal(manifest.exports['./package.json'], './package.json'); @@ -23,6 +24,14 @@ test('declares exactly the five public entrypoints and package metadata', () => assert.equal(manifest.types, 'lib/index.d.ts'); }); +test('the root entrypoint does not expose or initialize legacy components', async () => { + const root = await import('@fluentui-react-native/native-core'); + assert.ok(!('Callout' in root)); + assert.ok(!('FocusZone' in root)); + assert.ok(!('calloutName' in root)); + assert.ok(!('focusZoneName' in root)); +}); + for (const [entrypoint, path] of entrypoints) { test(`${entrypoint} has distinct source, JavaScript, and declaration targets`, async () => { const expected = { diff --git a/packages/agentic/native-core/tests/native-ownership.test.mjs b/packages/agentic/native-core/tests/native-ownership.test.mjs index bb24deee3d..12a85727fc 100644 --- a/packages/agentic/native-core/tests/native-ownership.test.mjs +++ b/packages/agentic/native-core/tests/native-ownership.test.mjs @@ -57,7 +57,7 @@ for (const shim of ['callout', 'focus-zone']) { const files = await readdir(root); assert.ok(!files.some((file) => file.endsWith('.podspec') || ['macos', 'windows', 'react-native.config.cjs'].includes(file))); const entrypoint = await readFile(new URL('src/index.ts', root), 'utf8'); - assert.match(entrypoint, /from '@fluentui-react-native\/native-core'/); + assert.match(entrypoint, /from '@fluentui-react-native\/native-core\/legacy'/); assert.doesNotMatch(entrypoint, /export \*/); }); } diff --git a/packages/agentic/storybook-desktop-runtime/src/Win32CalloutPortal.tsx b/packages/agentic/storybook-desktop-runtime/src/Win32CalloutPortal.tsx index af9f3eceb7..ee5b587a23 100644 --- a/packages/agentic/storybook-desktop-runtime/src/Win32CalloutPortal.tsx +++ b/packages/agentic/storybook-desktop-runtime/src/Win32CalloutPortal.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { StyleSheet } from 'react-native'; -import { Callout } from '@fluentui-react-native/native-core'; -import type { CalloutProps, DirectionalHint } from '@fluentui-react-native/native-core'; +import { Callout } from '@fluentui-react-native/native-core/legacy'; +import type { CalloutProps, DirectionalHint } from '@fluentui-react-native/native-core/legacy'; import { RootInputBoundary } from '@fluentui-react-native/design'; import { useTheme } from '@storybook/react-native-theming'; diff --git a/packages/deprecated/foundation-compose/AGENTS.md b/packages/deprecated/foundation-compose/AGENTS.md index 8a179372b2..218573b309 100644 --- a/packages/deprecated/foundation-compose/AGENTS.md +++ b/packages/deprecated/foundation-compose/AGENTS.md @@ -147,7 +147,7 @@ automatically. `ref` during preparation, commonly with `useViewCommandFocus`; type ref-bearing roots with `React.PropsWithRef<...>`. For imperative native APIs, use an internal native ref plus `useImperativeHandle`, as in - `packages/agentic/native-core/src/components/callout/Callout.tsx`. + `packages/agentic/native-core/src/legacy/callout/Callout.tsx`. - Do not introduce `forwardRef` or a new public `ref` convention into an established v0 API without an explicit migration requirement. diff --git a/packages/shim/callout/README.md b/packages/shim/callout/README.md index af29596036..cb7c14ee15 100644 --- a/packages/shim/callout/README.md +++ b/packages/shim/callout/README.md @@ -2,10 +2,10 @@ `@fluentui-react-native/callout` preserves its existing named exports by re-exporting the implementation and public types from -`@fluentui-react-native/native-core`. +`@fluentui-react-native/native-core/legacy`. New consumers should import `Callout`, `CalloutProps`, and `CalloutHandle` -from `@fluentui-react-native/native-core`. Deprecated compatibility aliases +from `@fluentui-react-native/native-core/legacy`. Deprecated compatibility aliases remain available through this shim. This package owns no native code, codegen specification, pod, or Windows @@ -14,5 +14,5 @@ as a direct dependency so native tooling discovers its pod and Windows Fabric library. Remove explicit `FRNCallout` pod declarations and regenerate autolinking/Pods after updating. -See the [Callout contract](../../agentic/native-core/src/components/callout/SPEC.md) +See the [Callout contract](../../agentic/native-core/src/legacy/callout/SPEC.md) and [native-core organization](../../agentic/native-core/README.md). diff --git a/packages/shim/callout/src/index.test.ts b/packages/shim/callout/src/index.test.ts index 134fd33cf1..15a107f5be 100644 --- a/packages/shim/callout/src/index.test.ts +++ b/packages/shim/callout/src/index.test.ts @@ -1,7 +1,7 @@ import { Callout, calloutName } from './index'; -import { Callout as NativeCallout, calloutName as nativeCalloutName } from '@fluentui-react-native/native-core'; +import { Callout as NativeCallout, calloutName as nativeCalloutName } from '@fluentui-react-native/native-core/legacy'; import type { CalloutHandle, CalloutProps, ICalloutProps, ICalloutTokens } from './index'; -import type { CalloutHandle as NativeCalloutHandle, CalloutProps as NativeCalloutProps } from '@fluentui-react-native/native-core'; +import type { CalloutHandle as NativeCalloutHandle, CalloutProps as NativeCalloutProps } from '@fluentui-react-native/native-core/legacy'; const props: ICalloutProps & ICalloutTokens = { directionalHint: 'bottomCenter' }; const nativeProps: NativeCalloutProps = props; diff --git a/packages/shim/callout/src/index.ts b/packages/shim/callout/src/index.ts index 235b9951ed..0b95238000 100644 --- a/packages/shim/callout/src/index.ts +++ b/packages/shim/callout/src/index.ts @@ -1,4 +1,4 @@ -export { Callout, calloutName } from '@fluentui-react-native/native-core'; +export { Callout, calloutName } from '@fluentui-react-native/native-core/legacy'; export type { CalloutHandle, CalloutNativeCommands, @@ -9,4 +9,4 @@ export type { ICalloutProps, ICalloutTokens, RestoreFocusEvent, -} from '@fluentui-react-native/native-core'; +} from '@fluentui-react-native/native-core/legacy'; diff --git a/packages/shim/focus-zone/README.md b/packages/shim/focus-zone/README.md index 69f23ccb57..83130a9cd7 100644 --- a/packages/shim/focus-zone/README.md +++ b/packages/shim/focus-zone/README.md @@ -2,11 +2,11 @@ `@fluentui-react-native/focus-zone` preserves its existing named exports by re-exporting the implementation and public types from -`@fluentui-react-native/native-core`. Its legacy `NativeProps` export aliases +`@fluentui-react-native/native-core/legacy`. Its legacy `NativeProps` export aliases the component-qualified `FocusZoneNativeProps` export. New consumers should import `FocusZone` and `FocusZoneProps` from -`@fluentui-react-native/native-core`. Deprecated compatibility types remain +`@fluentui-react-native/native-core/legacy`. Deprecated compatibility types remain available through this shim. This package owns no native code, codegen specification, pod, or Windows @@ -15,5 +15,5 @@ as a direct dependency so native tooling discovers its pod and Windows Fabric library. Remove explicit `RCTFocusZone` pod declarations and regenerate autolinking/Pods after updating. Win32 keeps its host-provided native component. -See the [FocusZone contract](../../agentic/native-core/src/components/focus-zone/SPEC.md) +See the [FocusZone contract](../../agentic/native-core/src/legacy/focus-zone/SPEC.md) and [native-core organization](../../agentic/native-core/README.md). diff --git a/packages/shim/focus-zone/src/index.test.ts b/packages/shim/focus-zone/src/index.test.ts index f7de321dbe..00c6905dd3 100644 --- a/packages/shim/focus-zone/src/index.test.ts +++ b/packages/shim/focus-zone/src/index.test.ts @@ -1,7 +1,7 @@ import { FocusZone, focusZoneName } from './index'; -import { FocusZone as NativeFocusZone, focusZoneName as nativeFocusZoneName } from '@fluentui-react-native/native-core'; +import { FocusZone as NativeFocusZone, focusZoneName as nativeFocusZoneName } from '@fluentui-react-native/native-core/legacy'; import type { FocusZoneProps, FocusZoneRenderData, FocusZoneState, FocusZoneTokens, FocusZoneType, NativeProps } from './index'; -import type { FocusZoneNativeProps, FocusZoneProps as NativeFocusZoneProps } from '@fluentui-react-native/native-core'; +import type { FocusZoneNativeProps, FocusZoneProps as NativeFocusZoneProps } from '@fluentui-react-native/native-core/legacy'; const props: FocusZoneProps = { focusZoneDirection: 'horizontal', isCircularNavigation: true }; const nativeProps: NativeFocusZoneProps = props; diff --git a/packages/shim/focus-zone/src/index.ts b/packages/shim/focus-zone/src/index.ts index 3c25d28d3d..53614aef8d 100644 --- a/packages/shim/focus-zone/src/index.ts +++ b/packages/shim/focus-zone/src/index.ts @@ -1,4 +1,4 @@ -export { FocusZone, focusZoneName } from '@fluentui-react-native/native-core'; +export { FocusZone, focusZoneName } from '@fluentui-react-native/native-core/legacy'; export type { FocusZoneDirection, FocusZoneProps, @@ -10,4 +10,4 @@ export type { FocusZoneType, FocusZoneNativeProps as NativeProps, NavigateAtEnd, -} from '@fluentui-react-native/native-core'; +} from '@fluentui-react-native/native-core/legacy'; From c42f54fb15fcbfcf15fc926ec7253c6000f6d147 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Fri, 2 Oct 2026 14:23:02 -0700 Subject: [PATCH 4/9] Fix native-core macOS interoperability and validate migration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/native-core-macos-interop.md | 5 +++ packages/agentic/native-core/README.md | 15 +++++++ .../components/Callout/shared/FRNCallout.h | 14 ------- .../native-core/macos/shared/FRNNativeCore.h | 2 + packages/agentic/native-core/package.json | 4 +- .../src/legacy/callout/Callout.stories.tsx | 40 ++++++++++++++++--- .../tests/native-ownership.test.mjs | 13 ++++++ .../agentic/native-core/tsconfig.stories.json | 19 +++++++++ yarn.lock | 1 + 9 files changed, 93 insertions(+), 20 deletions(-) create mode 100644 .changeset/native-core-macos-interop.md delete mode 100644 packages/agentic/native-core/macos/components/Callout/shared/FRNCallout.h create mode 100644 packages/agentic/native-core/macos/shared/FRNNativeCore.h create mode 100644 packages/agentic/native-core/tsconfig.stories.json diff --git a/.changeset/native-core-macos-interop.md b/.changeset/native-core-macos-interop.md new file mode 100644 index 0000000000..1395eefc1f --- /dev/null +++ b/.changeset/native-core-macos-interop.md @@ -0,0 +1,5 @@ +--- +'@fluentui-react-native/native-core': patch +--- + +Fix the macOS Swift interoperability header name for the consolidated native-core pod. diff --git a/packages/agentic/native-core/README.md b/packages/agentic/native-core/README.md index 6c741271fb..94f76086e5 100644 --- a/packages/agentic/native-core/README.md +++ b/packages/agentic/native-core/README.md @@ -38,6 +38,10 @@ explicit old `FRNCallout`/`RCTFocusZone` pod or standalone Windows project references and regenerate the consuming native project/Pods. Win32 continues to use its host-provided `RCTCallout` and `RCTFocusZone`. +The macOS module-named header at `macos/shared/FRNNativeCore.h` is required by +the generated `FRNNativeCore-Swift.h`; keep it aligned with the pod's +`module_name` when changing native ownership. + ## Public entrypoints | Import | Scope | Source entrypoint | @@ -234,3 +238,14 @@ The package prebuild regenerates Windows bindings from the shared spec. Native verification must cover registration, events/commands, refs, unmount cleanup, error propagation, and architecture/platform parity, not only JS mocks. +Callout's executable macOS Fabric story verifies native popup creation, `onShow` and +`onDismiss`, outside-click dismissal, and reopening. Run it together +with FocusZone's keyboard cases from `apps/storybook` using the owned lifecycle: + +```sh +STORYBOOK_SMOKE_STORY='native-*' yarn storybook smoke --macos --mode stories-and-tests +``` + +This traverses the full catalog before running the selected native tests. +The macOS Desktop Driver currently targets Fabric; a successful Paper build +does not establish Paper runtime or input parity. diff --git a/packages/agentic/native-core/macos/components/Callout/shared/FRNCallout.h b/packages/agentic/native-core/macos/components/Callout/shared/FRNCallout.h deleted file mode 100644 index 86c796ed89..0000000000 --- a/packages/agentic/native-core/macos/components/Callout/shared/FRNCallout.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// FRNCallout.h -// Pods -// -// Created by Patrick Boyd on 9/24/24. -// -// Created as a dummy file to enable importing FRNNativeCore-Swift.h and invoking Swift functions from Obj-C -// - -#ifndef FRNCallout_h -#define FRNCallout_h - - -#endif /* FRNCallout_h */ diff --git a/packages/agentic/native-core/macos/shared/FRNNativeCore.h b/packages/agentic/native-core/macos/shared/FRNNativeCore.h new file mode 100644 index 0000000000..8c039ee640 --- /dev/null +++ b/packages/agentic/native-core/macos/shared/FRNNativeCore.h @@ -0,0 +1,2 @@ +// The generated FRNNativeCore-Swift.h imports this module-named interoperability header. +#pragma once diff --git a/packages/agentic/native-core/package.json b/packages/agentic/native-core/package.json index b31e76f650..f73334bcbc 100644 --- a/packages/agentic/native-core/package.json +++ b/packages/agentic/native-core/package.json @@ -80,7 +80,8 @@ "format": "fluentui-scripts format", "lint": "fluentui-scripts lint", "prebuild": "yarn codegen:windows", - "test": "yarn build && node --test tests/entrypoints.test.mjs tests/native-ownership.test.mjs && fluentui-scripts jest", + "test": "yarn build && yarn test:stories && node --test tests/entrypoints.test.mjs tests/native-ownership.test.mjs && fluentui-scripts jest", + "test:stories": "tsc -p tsconfig.stories.json", "update-snapshots": "fluentui-scripts jest -u" }, "dependencies": { @@ -90,6 +91,7 @@ "devDependencies": { "@babel/core": "catalog:", "@fluentui-react-native/scripts": "workspace:*", + "@fluentui-react-native/storybook-desktop": "workspace:*", "@office-iss/react-native-win32": "^0.81.8", "@react-native-community/cli": "^20.0.0", "@react-native-community/cli-platform-android": "^20.0.0", diff --git a/packages/agentic/native-core/src/legacy/callout/Callout.stories.tsx b/packages/agentic/native-core/src/legacy/callout/Callout.stories.tsx index 6ae0691aaf..c139f7a9fb 100644 --- a/packages/agentic/native-core/src/legacy/callout/Callout.stories.tsx +++ b/packages/agentic/native-core/src/legacy/callout/Callout.stories.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { Pressable, StyleSheet, Text, View } from 'react-native'; +import type { WdioStory } from '@fluentui-react-native/storybook-desktop/testing'; import type { Meta, StoryObj } from '@storybook/react-native'; import { Callout } from './Callout'; @@ -48,6 +49,11 @@ const CalloutExample = ({ defaultVisible = false, onDismiss, onShow, showWindowC return ( + + + Native window: {status} + + {visible ? 'Close callout' : 'Open callout'} - - Native window: {status} - {visible && ( @@ -177,10 +180,37 @@ const meta: Meta = { export default meta; -type Story = StoryObj; +type Story = WdioStory>; export const Default: Story = { + tags: ['desktop-e2e'], render: (args) => , + wdio: { + 'macOS opens, dismisses, and recreates a native popup window': async ({ browser, expect, platform, skip }) => { + if (platform !== 'macos') { + skip('This case verifies the AppKit popup-window lifecycle.'); + return; + } + const features = browser.capabilities['furn:features']; + if (!features?.physicalClick) { + skip('Popup lifecycle qualification requires physical pointer input.'); + return; + } + for (let attempt = 0; attempt < 2; attempt++) { + await expect(await browser.$('~agentic-storybook-callout-status')).toHaveText('Native window: Shown'); + expect(await browser.getWindowHandles()).toHaveLength(2); + await (await browser.$('~agentic-storybook-callout-status')).click(); + await browser.waitUntil(async () => (await browser.getWindowHandles()).length === 1, { + timeout: 5000, + timeoutMsg: 'Expected dismissal to remove the native Callout window.', + }); + await expect(await browser.$('~agentic-storybook-callout-status')).toHaveText('Native window: Dismissed'); + if (attempt === 0) { + await (await browser.$('~agentic-storybook-callout-trigger')).click(); + } + } + }, + }, }; export const Placement: Story = { @@ -252,7 +282,7 @@ const styles = StyleSheet.create({ }, status: { color: '#616161', - marginTop: 12, + marginBottom: 12, }, story: { alignItems: 'center', diff --git a/packages/agentic/native-core/tests/native-ownership.test.mjs b/packages/agentic/native-core/tests/native-ownership.test.mjs index 12a85727fc..ceb5ab99ce 100644 --- a/packages/agentic/native-core/tests/native-ownership.test.mjs +++ b/packages/agentic/native-core/tests/native-ownership.test.mjs @@ -47,6 +47,19 @@ test('the shared macOS pod includes both components and retains both renderer ad } }); +test('macOS Swift interoperability uses the consolidated pod module name', async () => { + const podspec = await readFile(new URL('FRNNativeCore.podspec', packageRoot), 'utf8'); + const moduleName = podspec.match(/s\.module_name\s*=\s*'([^']+)'/)?.[1]; + assert.equal(moduleName, 'FRNNativeCore'); + await readFile(new URL(`macos/shared/${moduleName}.h`, packageRoot), 'utf8'); + + for (const adapter of ['fabric/RCTCalloutComponentView.mm', 'paper/FRNCalloutManager.m']) { + const source = await readFile(new URL(`macos/components/Callout/${adapter}`, packageRoot), 'utf8'); + assert.ok(source.includes(`#import "${moduleName}-Swift.h"`)); + assert.doesNotMatch(source, /FRNCallout-Swift\.h/); + } +}); + for (const shim of ['callout', 'focus-zone']) { test(`${shim} is a JS-only compatibility shim with no duplicate native ownership`, async () => { const root = new URL(`../../shim/${shim}/`, packageRoot); diff --git a/packages/agentic/native-core/tsconfig.stories.json b/packages/agentic/native-core/tsconfig.stories.json new file mode 100644 index 0000000000..40b8e99540 --- /dev/null +++ b/packages/agentic/native-core/tsconfig.stories.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "composite": false, + "noImplicitAny": true, + "noEmit": true, + "tsBuildInfoFile": ".cache/tsconfig.stories.tsbuildinfo" + }, + "include": ["src/legacy/callout/Callout.stories.tsx"], + "exclude": [], + "references": [ + { + "path": "./tsconfig.json" + }, + { + "path": "../storybook-desktop/tsconfig.json" + } + ] +} diff --git a/yarn.lock b/yarn.lock index 3273416e4e..3f73143881 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4519,6 +4519,7 @@ __metadata: "@fluentui-react-native/framework-base": "workspace:*" "@fluentui-react-native/interactive-hooks": "workspace:*" "@fluentui-react-native/scripts": "workspace:*" + "@fluentui-react-native/storybook-desktop": "workspace:*" "@office-iss/react-native-win32": "npm:^0.81.8" "@react-native-community/cli": "npm:^20.0.0" "@react-native-community/cli-platform-android": "npm:^20.0.0" From cba3377f4fce1ed187edf141e081f9a8ca472675 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Fri, 2 Oct 2026 16:03:05 -0700 Subject: [PATCH 5/9] fix tsconfig configuration --- packages/agentic/native-core/tsconfig.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/agentic/native-core/tsconfig.json b/packages/agentic/native-core/tsconfig.json index bd2b34cb96..b8fd9ae534 100644 --- a/packages/agentic/native-core/tsconfig.json +++ b/packages/agentic/native-core/tsconfig.json @@ -17,6 +17,9 @@ }, { "path": "../../../scripts/tsconfig.json" + }, + { + "path": "../storybook-desktop/tsconfig.json" } ] } From 98ef78f7cc1ef8cad85caea70081aee1af5b07a4 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Fri, 2 Oct 2026 16:39:28 -0700 Subject: [PATCH 6/9] add the ability to run the macos storybook app in paper mode and add a pipeline run for it --- .changeset/macos-paper-storybook.md | 8 ++ .github/workflows/pr.yml | 51 +++++++ apps/storybook/AGENTS.md | 2 + apps/storybook/README.md | 23 +++ apps/storybook/agent-map.yaml | 5 + apps/storybook/macos/Podfile | 2 +- .../compound-item-layout.stories.tsx | 5 +- .../compound-item-layout.test.tsx | 35 ++++- packages/agentic/native-core/tsconfig.json | 3 + packages/agentic/storybook-desktop/README.md | 8 ++ .../src/cli/DesktopStorybookCli.test.ts | 133 ++++++++++++++++++ .../src/cli/DesktopStorybookCli.ts | 18 ++- .../storybook-desktop/src/cli/README.md | 7 + .../src/cli/createDesktopStorybookCommand.ts | 55 ++++---- .../src/driver/driverManifest.test.ts | 24 ++++ .../src/driver/driverManifest.ts | 4 +- 16 files changed, 351 insertions(+), 32 deletions(-) create mode 100644 .changeset/macos-paper-storybook.md diff --git a/.changeset/macos-paper-storybook.md b/.changeset/macos-paper-storybook.md new file mode 100644 index 0000000000..0f1683eff8 --- /dev/null +++ b/.changeset/macos-paper-storybook.md @@ -0,0 +1,8 @@ +--- +'@fluentui-react-native/storybook-desktop': minor +'@fluentui-react-native/components': patch +--- + +Add a macOS-only `--paper` option to prepare, build, launch, and smoke test Storybook with Paper while preserving Fabric as the default renderer. + +Use centered alignment in macOS Paper CompoundItemLayout stories to avoid the native Yoga baseline crash without changing the primitive or Fabric demonstrations. diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 4c91383c03..552d718b37 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -244,6 +244,57 @@ jobs: apps/storybook/artifacts/storybook-commands ${{ steps.desktop-driver.outputs.doctor-path }} + macos-paper-storybook: + name: macOS Paper Storybook PR + runs-on: macos-26 + timeout-minutes: 40 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Set up toolchain + uses: microsoft/react-native-test-app/.github/actions/setup-toolchain@c75754b0fedce160a4dcf7193fae1a6ece76efe2 # 5.4.11 + with: + platform: macos + node-version: 22 + + - name: Install dependencies + run: yarn + + - name: Build packages + run: yarn build + + - name: Set up macOS Desktop Driver + id: desktop-driver + uses: ./.github/actions/setup-desktop-driver + with: + platform: macos + disabled-input-features: physicalClick + + - name: Bundle macOS Paper + run: yarn storybook bundle --macos --paper + working-directory: apps/storybook + + - name: Pod install (Paper) + run: yarn storybook prep --macos --paper + working-directory: apps/storybook + + - name: Run through Paper storybook smoke tests + run: yarn storybook smoke --macos --paper --mode stories-and-tests + working-directory: apps/storybook + env: + CCACHE_DISABLE: 1 + + - name: Upload macOS Paper Storybook artifacts + if: ${{ always() }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: Storybook_macos_Paper_Dump + path: | + apps/storybook/artifacts/macos + apps/storybook/artifacts/storybook-commands + ${{ steps.desktop-driver.outputs.doctor-path }} + ios: name: iOS PR runs-on: macos-26 diff --git a/apps/storybook/AGENTS.md b/apps/storybook/AGENTS.md index 278e70f281..37f5a638fa 100644 --- a/apps/storybook/AGENTS.md +++ b/apps/storybook/AGENTS.md @@ -82,6 +82,8 @@ Read [`agent-map.yaml`](agent-map.yaml) first for the compact architecture, look repository root because subprocess dependency resolution must start in this workspace. - Run `yarn storybook bundle --macos` for the JavaScript bundle, `yarn storybook build --macos` for a non-launching native build, and `yarn storybook smoke --macos` for the complete owned lifecycle. +- macOS defaults to Fabric. Add `--paper` to `prep`, `build`, `run`, or `smoke` for Paper; regenerate with `prep` + whenever switching renderers because both modes share the generated project and Pods. Keep the endpoint `macos`. - Preserve the shared smoke instance context: its canonical-root hash coordinates the macOS bundle identifier, Storybook port, Metro port, generated runtime polyfill, and exact app shutdown. Do not replace those values with process-name matching or fixed smoke ports. diff --git a/apps/storybook/README.md b/apps/storybook/README.md index b34184c251..3bcb906e90 100644 --- a/apps/storybook/README.md +++ b/apps/storybook/README.md @@ -95,6 +95,29 @@ yarn storybook run --macos Requires Xcode + CocoaPods. +Fabric is the default macOS renderer. Add `--paper` to use Paper with the same +macOS story catalog and native Desktop Driver: + +```sh +yarn storybook prep --macos --paper +yarn storybook build --macos --paper +yarn storybook smoke --macos --paper --mode stories-and-tests +``` + +For interactive development, use `yarn storybook driver --macos --paper` and +`yarn storybook run --macos --paper` in separate terminals. `--paper` is supported +only on macOS; it sets `RCT_NEW_ARCH_ENABLED=0` for child commands. Omitting it +selects Fabric explicitly, even if the parent environment disables the New Architecture. +Run `prep` again whenever switching renderers: the generated Xcode project and +Pods are shared, so Paper and Fabric cannot run concurrently from one enlistment. +To return to Fabric, run `yarn storybook prep --macos` before building or launching. +The **macOS Paper Storybook PR** job runs the full catalog traversal and authored +tests separately from the existing Fabric job. +CompoundItemLayout's demos use centered alignment in macOS Paper because the +current native text-baseline callback can return NaN and terminate Yoga +([upstream issue](https://github.com/microsoft/react-native-macos/issues/3121)). +Fabric and other platforms retain the baseline demonstration. The primitive itself is unchanged. + Run `yarn storybook smoke --macos` for the complete server, Metro, build/launch, all-story traversal, and ownership-safe shutdown lifecycle. The shared CLI hashes this enlistment's canonical project root, uses that suffix in the native bundle identifier, and selects dedicated Storybook and Metro ports. diff --git a/apps/storybook/agent-map.yaml b/apps/storybook/agent-map.yaml index 8039f0170e..c70de0a9f6 100644 --- a/apps/storybook/agent-map.yaml +++ b/apps/storybook/agent-map.yaml @@ -65,6 +65,11 @@ native: disabled_input_env: FURN_DESKTOP_DRIVER_DISABLED_INPUT_FEATURES ci_policy: explicit-build-then-prebuilt-only macos: + renderer: + default: fabric + paper_flag: --paper + switching: rerun-prep-before-build-or-launch + concurrency: one-renderer-per-enlistment process: ReactTestApp app_bundle: macos/DerivedData/Build/Products/Debug/ReactTestApp.app coordinate_notes: diff --git a/apps/storybook/macos/Podfile b/apps/storybook/macos/Podfile index 731562ecad..b6d52576af 100644 --- a/apps/storybook/macos/Podfile +++ b/apps/storybook/macos/Podfile @@ -32,7 +32,7 @@ end # react-native-test-app runs codegen before autolinking, so seed its clean-install input first. write_storybook_autolinking_config!(File.expand_path('..', __dir__)) -use_test_app!(:fabric_enabled => true, :bridgeless_enabled => false) do |target| +use_test_app!(:fabric_enabled => ENV.fetch('RCT_NEW_ARCH_ENABLED', '1') != '0', :bridgeless_enabled => false) do |target| target.app do platform :osx, '14' end diff --git a/packages/agentic/components/src/primitives/compound-item-layout/compound-item-layout.stories.tsx b/packages/agentic/components/src/primitives/compound-item-layout/compound-item-layout.stories.tsx index 9497fdb885..6654613fc2 100644 --- a/packages/agentic/components/src/primitives/compound-item-layout/compound-item-layout.stories.tsx +++ b/packages/agentic/components/src/primitives/compound-item-layout/compound-item-layout.stories.tsx @@ -1,6 +1,6 @@ /** @jsxImportSource @fluentui-react-native/framework-base */ import type { ReactNode } from 'react'; -import { StyleSheet, Text, View } from 'react-native'; +import { Platform, StyleSheet, Text, View } from 'react-native'; import type { Meta, StoryObj } from '@storybook/react-native'; @@ -55,7 +55,8 @@ const styles = StyleSheet.create({ width: 360, }, textBaseline: { - alignItems: 'baseline', + // macOS Paper baseline crash: https://github.com/microsoft/react-native-macos/issues/3121 + alignItems: Platform.OS === 'macos' && Reflect.get(globalThis, 'nativeFabricUIManager') == null ? 'center' : 'baseline', }, leading: { alignItems: 'center', diff --git a/packages/agentic/components/src/primitives/compound-item-layout/compound-item-layout.test.tsx b/packages/agentic/components/src/primitives/compound-item-layout/compound-item-layout.test.tsx index 0a2e5c0d0c..718ddbd318 100644 --- a/packages/agentic/components/src/primitives/compound-item-layout/compound-item-layout.test.tsx +++ b/packages/agentic/components/src/primitives/compound-item-layout/compound-item-layout.test.tsx @@ -1,5 +1,5 @@ /** @jsxImportSource @fluentui-react-native/framework-base */ -import { StyleSheet, Text } from 'react-native'; +import { Platform, StyleSheet, Text } from 'react-native'; import { render } from '../../common/renderWithTheme'; @@ -42,4 +42,37 @@ describe('CompoundItemLayout', () => { await component.rerender(Primary} testID="text-row" />); expect(StyleSheet.flatten(component.getByTestId('text-row').props.style)).toMatchObject({ alignItems: 'center' }); }); + + it.each([ + ['macos', false, 'center'], + ['macos', true, 'baseline'], + ['windows', true, 'baseline'], + ['win32', false, 'baseline'], + ])('uses %s story alignment with Fabric=%s', (platform, fabric, alignment) => { + const descriptor = Object.getOwnPropertyDescriptor(globalThis, 'nativeFabricUIManager'); + const create = jest.fn(StyleSheet.create); + Object.defineProperty(globalThis, 'nativeFabricUIManager', { configurable: true, value: fabric ? {} : undefined }); + + try { + jest.isolateModules(() => { + jest.doMock('react-native', () => + Object.create(jest.requireActual('react-native'), { + Platform: { value: { ...Platform, OS: platform } }, + StyleSheet: { value: { ...StyleSheet, create } }, + }), + ); + require('./compound-item-layout.stories'); + }); + expect(create.mock.calls.map(([styles]) => styles)).toEqual( + expect.arrayContaining([expect.objectContaining({ textBaseline: { alignItems: alignment } })]), + ); + } finally { + jest.dontMock('react-native'); + if (descriptor) { + Object.defineProperty(globalThis, 'nativeFabricUIManager', descriptor); + } else { + Reflect.deleteProperty(globalThis, 'nativeFabricUIManager'); + } + } + }); }); diff --git a/packages/agentic/native-core/tsconfig.json b/packages/agentic/native-core/tsconfig.json index bd2b34cb96..b8fd9ae534 100644 --- a/packages/agentic/native-core/tsconfig.json +++ b/packages/agentic/native-core/tsconfig.json @@ -17,6 +17,9 @@ }, { "path": "../../../scripts/tsconfig.json" + }, + { + "path": "../storybook-desktop/tsconfig.json" } ] } diff --git a/packages/agentic/storybook-desktop/README.md b/packages/agentic/storybook-desktop/README.md index 57ce034bb3..0d337b2f91 100644 --- a/packages/agentic/storybook-desktop/README.md +++ b/packages/agentic/storybook-desktop/README.md @@ -129,6 +129,14 @@ storybook-desktop smoke --windows --mode stories-and-tests ``` Use `--config ` for a differently named configuration file. +macOS defaults to Fabric. Add `--paper` to macOS commands to set +`RCT_NEW_ARCH_ENABLED=0` for their child processes; commands without the flag +explicitly select Fabric. The endpoint, story catalog, and native driver remain +`macos`. Consuming Podfiles must honor `RCT_NEW_ARCH_ENABLED` when passing +`fabric_enabled` to `use_test_app!`. Run `prep` with the desired flag whenever +switching renderers because the generated native project and Pods are shared. +The matching API option is `new DesktopStorybookCli(config, { paper: true })`. + `build-driver` builds only the source-shipped native helper. `prep` first ensures that helper, then installs CocoaPods on macOS or generates the React Native Test App solution on Windows; Win32 prep now ensures the shared Windows diff --git a/packages/agentic/storybook-desktop/src/cli/DesktopStorybookCli.test.ts b/packages/agentic/storybook-desktop/src/cli/DesktopStorybookCli.test.ts index eba279386d..5dd87cd1c8 100644 --- a/packages/agentic/storybook-desktop/src/cli/DesktopStorybookCli.test.ts +++ b/packages/agentic/storybook-desktop/src/cli/DesktopStorybookCli.test.ts @@ -286,6 +286,58 @@ describe('DesktopStorybookCli', () => { args: ['run', '--platform', 'windows', '--solution', 'windows/AgenticStorybook.sln'], }); await expect(cli.build('win32')).rejects.toThrow('build is not configured for win32'); + expect(runner.foreground[0].env).not.toHaveProperty('RCT_NEW_ARCH_ENABLED'); + }); + + test.each([true, false])('applies the macOS renderer to every command with paper=%s', async (paper) => { + const runner = new RecordingRunner(); + const cli = new DesktopStorybookCli(makeConfig(), { ...nativeDriverTestOptions, paper, runner }); + + await cli.prep('macos', { driver: false }); + await cli.bundle('macos'); + await cli.build('macos'); + await cli.run('macos'); + + expect(runner.foreground.map(({ command }) => command)).toEqual(['pod', 'sb-rn-get-stories', 'rnx-cli', 'rnx-cli', 'rnx-cli']); + expect(runner.foreground.every(({ env }) => env.RCT_NEW_ARCH_ENABLED === (paper ? '0' : '1'))).toBe(true); + }); + + test('switches back to Fabric explicitly after Paper preparation', async () => { + const runner = new RecordingRunner(); + const paper = new DesktopStorybookCli(makeConfig(), { ...nativeDriverTestOptions, paper: true, runner }); + const fabric = new DesktopStorybookCli(makeConfig(), { ...nativeDriverTestOptions, runner }); + + await paper.prep('macos', { driver: false }); + await fabric.prep('macos', { driver: false }); + + expect(runner.foreground.map(({ env }) => env.RCT_NEW_ARCH_ENABLED)).toEqual(['0', '1']); + }); + + test('rotates the authenticated bridge nonce when switching macOS renderers', async () => { + const options = { + ...nativeDriverTestOptions, + createStoryManifest: createEmptyStoryManifest, + isPortAvailable: async () => true, + output: { write: () => true }, + runner: new RecordingRunner(), + }; + const manifestPath = path.join(storybookRoot, 'storybook-desktop.generated', 'driver-manifest.macos.json'); + + try { + await new DesktopStorybookCli(makeConfig(), options).driver('macos'); + const fabricManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); + await new DesktopStorybookCli(makeConfig(), { ...options, paper: true }).driver('macos'); + const paperManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); + await new DesktopStorybookCli(makeConfig(), { ...options, paper: true }).driver('macos'); + const repeatedPaperManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); + + expect(fabricManifest.renderer).toBe('fabric'); + expect(paperManifest.renderer).toBe('paper'); + expect(paperManifest.bridgeNonce).not.toBe(fabricManifest.bridgeNonce); + expect(repeatedPaperManifest.bridgeNonce).toBe(paperManifest.bridgeNonce); + } finally { + fs.rmSync(manifestPath, { force: true }); + } }); test('runs the standalone macOS app with the enlistment-specific identity', async () => { @@ -655,6 +707,87 @@ describe('DesktopStorybookCli', () => { }); describe('createDesktopStorybookCommand', () => { + test.each(['prep', 'bundle', 'build', 'run'] as const)('forwards --paper to macOS %s', async (action) => { + const runner = new RecordingRunner(); + const program = createDesktopStorybookCommand({ ...nativeDriverTestOptions, config: makeConfig(), runner }); + + await program.parseAsync(['node', 'storybook', action, '--macos', '--paper', ...(action === 'prep' ? ['--no-driver'] : [])]); + + expect(runner.foreground.length).toBeGreaterThan(0); + expect(runner.foreground.every(({ env }) => env.RCT_NEW_ARCH_ENABLED === '0' && env[FURN_STORYBOOK_PLATFORM] === 'macos')).toBe(true); + }); + + test('keeps the Paper smoke endpoint and propagates the renderer through launch and cleanup', async () => { + const runner = new RecordingRunner(); + const runSmokeTests = jest.fn(async () => ({ + endpoint: 'macos' as const, + finishedAt: '2026-10-02T08:00:01.000Z', + manifest: { platform: 'macos-digest', portable: 'portable-digest' }, + platformName: 'macos' as const, + runId: 'paper-smoke', + schemaVersion: 1 as const, + startedAt: '2026-10-02T08:00:00.000Z', + status: 'passed' as const, + targetId: 'agenticstorybook-macos', + tests: [], + })); + const program = createDesktopStorybookCommand({ + ...nativeDriverTestOptions, + config: makeConfig({ macos: { run: { command: 'launch-storybook' }, smoke: { stop: { command: 'stop-storybook' } } } }), + createStoryManifest: createEmptyStoryManifest, + fetch: async () => new Response(JSON.stringify({ entries: { story: { id: 'first--story', type: 'story' } } })), + isPortAvailable: async () => true, + output: { write: () => true }, + runner, + runSmokeTests, + }); + + await program.parseAsync(['node', 'storybook', 'smoke', '--macos', '--paper', '--mode', 'stories-and-tests']); + + expect(runner.foreground.map(({ command }) => command)).toEqual(['launch-storybook', 'stop-storybook']); + expect([...runner.foreground, ...runner.background].every(({ env }) => env.RCT_NEW_ARCH_ENABLED === '0')).toBe(true); + expect(runSmokeTests).toHaveBeenCalledWith(expect.objectContaining({ platform: 'macos', targetId: 'agenticstorybook-macos' })); + expect( + JSON.parse(fs.readFileSync(path.join(storybookRoot, 'storybook-desktop.generated', 'driver-manifest.macos.json'), 'utf8')), + ).toMatchObject({ + endpoint: 'macos', + renderer: 'paper', + }); + expect(runner.stopped).toBe(2); + }); + + test.each(['windows', 'win32'])('rejects --paper with explicit --%s', async (platform) => { + const runner = new RecordingRunner(); + const program = createDesktopStorybookCommand({ config: makeConfig(), runner }); + program.commands.forEach((command) => { + command.exitOverride(); + command.configureOutput({ writeErr: () => {} }); + }); + + await expect(program.parseAsync(['node', 'storybook', 'build', `--${platform}`, '--paper'])).rejects.toThrow( + 'cannot be used with option', + ); + expect(runner.foreground).toEqual([]); + }); + + test.each(['windows', 'win32'])('rejects --paper with the implicit %s endpoint', async (platform) => { + const previousPlatform = process.env[FURN_STORYBOOK_PLATFORM]; + process.env[FURN_STORYBOOK_PLATFORM] = platform; + const runner = new RecordingRunner(); + const program = createDesktopStorybookCommand({ config: makeConfig(), runner }); + + try { + await expect(program.parseAsync(['node', 'storybook', 'build', '--paper'])).rejects.toThrow('--paper is only supported on macOS'); + expect(runner.foreground).toEqual([]); + } finally { + if (previousPlatform === undefined) { + delete process.env[FURN_STORYBOOK_PLATFORM]; + } else { + process.env[FURN_STORYBOOK_PLATFORM] = previousPlatform; + } + } + }); + test('forwards verbose logging through nested lifecycle commands', async () => { const runner = new RecordingRunner(); const command = createDesktopStorybookCommand({ config: makeConfig(), runner }); diff --git a/packages/agentic/storybook-desktop/src/cli/DesktopStorybookCli.ts b/packages/agentic/storybook-desktop/src/cli/DesktopStorybookCli.ts index 314cb13648..4dd4d310be 100644 --- a/packages/agentic/storybook-desktop/src/cli/DesktopStorybookCli.ts +++ b/packages/agentic/storybook-desktop/src/cli/DesktopStorybookCli.ts @@ -52,6 +52,8 @@ export type DesktopStorybookPrepOptions = { }; export type DesktopStorybookCliOptions = { + /** Use the macOS Paper renderer instead of the default Fabric renderer. */ + paper?: boolean; buildNativeDriver?: typeof buildNativeDesktopDriver; createStoryManifest?: typeof createDesktopStoryManifest; runner?: DesktopCommandRunner; @@ -79,6 +81,7 @@ export type DesktopStorybookTestOptions = DesktopStorybookWdioOptions & { export class DesktopStorybookCli { readonly config: DesktopStorybookConfig; readonly instance: DesktopStorybookInstance; + readonly paper: boolean; private readonly runner: DesktopCommandRunner; private readonly buildNativeDriver: typeof buildNativeDesktopDriver; @@ -94,6 +97,7 @@ export class DesktopStorybookCli { constructor(config: DesktopStorybookConfig, options: DesktopStorybookCliOptions = {}) { this.config = config; + this.paper = options.paper ?? false; this.instance = createDesktopStorybookInstance({ projectRoot: config.projectRoot, bundleIdentifierPrefix: config.macosBundleIdentifier, @@ -256,6 +260,8 @@ export class DesktopStorybookCli { manifest.endpoint !== platform || !('instanceId' in manifest) || manifest.instanceId !== this.instance.id || + !('renderer' in manifest) || + manifest.renderer !== this.renderer(platform) || !('driverPort' in manifest) || typeof manifest.driverPort !== 'number' || !Number.isInteger(manifest.driverPort) || @@ -275,6 +281,7 @@ export class DesktopStorybookCli { { ...this.instance, endpoint: platform, + renderer: this.renderer(platform), targetId: `${this.config.appName}-${platform}`.toLowerCase(), testIDPrefix: this.config.testIDPrefix, }, @@ -489,18 +496,24 @@ export class DesktopStorybookCli { ): Promise { const storyManifest = await this.createStoryManifest(this.config, platform); const outputPath = path.join(this.config.projectRoot, 'storybook-desktop.generated', `driver-manifest.${platform}.json`); + const renderer = this.renderer(platform); const driverManifest = createDesktopStorybookDriverManifest({ - bridgeNonce: readReusableBridgeNonce(outputPath, instance, storyManifest.platformManifestDigest), + bridgeNonce: readReusableBridgeNonce(outputPath, instance, storyManifest.platformManifestDigest, renderer), config: this.config, instance, nativeDriver, platform, + renderer, storyManifest, }); writeDesktopStorybookDriverManifest(driverManifest, outputPath); return outputPath; } + private renderer(platform: Platforms): 'fabric' | 'paper' { + return platform === 'win32' || (platform === 'macos' && this.paper) ? 'paper' : 'fabric'; + } + private async resolveDriver(platform: Platforms): Promise { const options = this.requireNativeDriverOptions(platform); return this.resolveNativeDriver({ @@ -655,6 +668,7 @@ export class DesktopStorybookCli { env: { ...command.env, [FURN_STORYBOOK_PLATFORM]: platform, + ...(platform === 'macos' ? { RCT_NEW_ARCH_ENABLED: this.paper ? '0' : '1' } : {}), [STORYBOOK_VERBOSE]: this.verbose ? '1' : '0', ...(instance ? { @@ -676,6 +690,7 @@ function readReusableBridgeNonce( manifestPath: string, instance: DesktopStorybookInstance, platformManifestDigest: string, + renderer: 'fabric' | 'paper', ): string | undefined { if (!fs.existsSync(manifestPath)) { return undefined; @@ -685,6 +700,7 @@ function readReusableBridgeNonce( return current.instanceId === instance.id && current.storybookPort === instance.storybookPort && current.driverPort === instance.driverPort && + current.renderer === renderer && current.platformManifestDigest === platformManifestDigest && typeof current.bridgeNonce === 'string' ? current.bridgeNonce diff --git a/packages/agentic/storybook-desktop/src/cli/README.md b/packages/agentic/storybook-desktop/src/cli/README.md index decc16468f..6fe4534865 100644 --- a/packages/agentic/storybook-desktop/src/cli/README.md +++ b/packages/agentic/storybook-desktop/src/cli/README.md @@ -38,6 +38,13 @@ Win32 is never selected implicitly because a Windows machine may contain both the Windows Fabric app and a Win32 Paper host. Use `--win32` for one command or set `FURN_STORYBOOK_PLATFORM=win32` for a multi-process workflow. +macOS uses Fabric unless `--paper` is present. Use +`yarn storybook prep --macos --paper`, then pass `--paper` to `build`, `run`, +or `smoke` to exercise Paper. Regenerate with `prep` whenever switching modes; +the Xcode project and Pods are shared, not parallel renderer-specific outputs. +Both modes retain the `macos` endpoint and story catalog. `--paper` is rejected +for Windows and Win32. + Prefer an explicit option for isolated commands: ```sh diff --git a/packages/agentic/storybook-desktop/src/cli/createDesktopStorybookCommand.ts b/packages/agentic/storybook-desktop/src/cli/createDesktopStorybookCommand.ts index 21de734d72..479df1e533 100644 --- a/packages/agentic/storybook-desktop/src/cli/createDesktopStorybookCommand.ts +++ b/packages/agentic/storybook-desktop/src/cli/createDesktopStorybookCommand.ts @@ -15,6 +15,7 @@ import { loadDesktopStorybookConfig } from './loadConfig.js'; type PlatformFlags = { macos?: boolean; + paper?: boolean; win32?: boolean; windows?: boolean; }; @@ -24,6 +25,7 @@ type ManifestFlags = PlatformFlags & { out?: string }; type SmokeFlags = PlatformFlags & { mode: DesktopSmokeMode }; type BuildDriverFlags = PlatformFlags & DesktopStorybookBuildDriverOptions; type PrepFlags = PlatformFlags & { driver: DesktopStorybookPrepOptions['driver'] }; +type GetApi = (flags: PlatformFlags) => Promise; export type CreateDesktopStorybookCommandOptions = DesktopStorybookCliOptions & { config?: DesktopStorybookConfig; @@ -38,7 +40,7 @@ export function createDesktopStorybookCommand(options: CreateDesktopStorybookCom .option('-v, --verbose', 'replay full captured output for successful commands as well as failures'); let apiPromise: Promise | undefined; - const getApi = () => + const getApi = (flags: PlatformFlags) => (apiPromise ??= Promise.resolve( options.config ?? loadDesktopStorybookConfig(program.opts<{ config?: string }>().config, options.cwd), ).then( @@ -50,10 +52,12 @@ export function createDesktopStorybookCommand(options: CreateDesktopStorybookCom fetch: options.fetch, output: options.output, errorOutput: options.errorOutput, + paper: flags.paper ?? options.paper, verbose: program.opts<{ verbose?: boolean }>().verbose ?? options.verbose, isPortAvailable: options.isPortAvailable, runSmokeTests: options.runSmokeTests, resolveNativeDriver: options.resolveNativeDriver, + writeMacOSApplicationLease: options.writeMacOSApplicationLease, }), )); @@ -72,7 +76,7 @@ export function createDesktopStorybookCommand(options: CreateDesktopStorybookCom return program; } -function addTestCommand(program: Command, getApi: () => Promise): void { +function addTestCommand(program: Command, getApi: GetApi): void { const command = program .command('test') .description('Run executable wdio story callbacks against the running Storybook app.') @@ -87,7 +91,7 @@ function addTestCommand(program: Command, getApi: () => Promise', 'native click semantics').choices(['auto', 'physical', 'accessibility'])); addPlatformOptions(command); command.action(async (flags: PlatformFlags & DesktopStorybookTestOptions) => { - const api = await getApi(); + const api = await getApi(flags); await api.test(resolvePlatform(flags, api), { clickMode: flags.clickMode, list: flags.list, @@ -102,29 +106,29 @@ function addTestCommand(program: Command, getApi: () => Promise Promise): void { +function addPrepCommand(program: Command, getApi: GetApi): void { const command = program .command('prep') .description('Prepare the native helper, dependencies, and generated projects.') .option('--no-driver', 'prepare only the native app project'); addPlatformOptions(command); command.action(async (flags: PrepFlags) => { - const api = await getApi(); + const api = await getApi(flags); await api.prep(resolvePlatform(flags, api), { driver: flags.driver }); }); } -function addBuildDriverCommand(program: Command, getApi: () => Promise): void { +function addBuildDriverCommand(program: Command, getApi: GetApi): void { const command = program.command('build-driver').description('Build the native Desktop Driver helper without preparing the app.'); command.option('--force', 'publish a new immutable helper selection'); addPlatformOptions(command); command.action(async (flags: BuildDriverFlags) => { - const api = await getApi(); + const api = await getApi(flags); await api.buildDriver(resolvePlatform(flags, api), { force: flags.force }); }); } -function addDriverCommand(program: Command, getApi: () => Promise): void { +function addDriverCommand(program: Command, getApi: GetApi): void { const command = program .command('driver') .description('Start the Storybook channel, MCP, and embedded Desktop Driver servers.') @@ -132,25 +136,25 @@ function addDriverCommand(program: Command, getApi: () => Promise', 'Storybook channel port; defaults to the enlistment-specific port', parsePort); addPlatformOptions(command); command.action(async (flags: ServerFlags) => { - const api = await getApi(); + const api = await getApi(flags); await api.driver(resolvePlatform(flags, api), { host: flags.host, port: flags.port }); }); } -function addManifestCommand(program: Command, getApi: () => Promise): void { +function addManifestCommand(program: Command, getApi: GetApi): void { const command = program.command('manifest').description('Generate the platform Story Manifest.').option('--out ', 'output path'); addPlatformOptions(command); command.action(async (flags: ManifestFlags) => { - const api = await getApi(); + const api = await getApi(flags); await api.manifest(resolvePlatform(flags, api), flags.out); }); } -function addInstanceCommand(program: Command, getApi: () => Promise): void { +function addInstanceCommand(program: Command, getApi: GetApi): void { const command = program.command('instance').description('Print the platform instance identity as JSON.'); addPlatformOptions(command); command.action(async (flags: PlatformFlags) => { - const api = await getApi(); + const api = await getApi(flags); api.printInstance(resolvePlatform(flags, api)); }); } @@ -159,22 +163,17 @@ export async function runDesktopStorybookCli(argv: readonly string[] = process.a await createDesktopStorybookCommand().parseAsync([...argv]); } -function addActionCommand( - program: Command, - action: 'bundle' | 'run' | 'build', - description: string, - getApi: () => Promise, -): void { +function addActionCommand(program: Command, action: 'bundle' | 'run' | 'build', description: string, getApi: GetApi): void { const command = program.command(action).description(description); addPlatformOptions(command); command.action(async (flags: PlatformFlags) => { - const api = await getApi(); + const api = await getApi(flags); const platform = resolvePlatform(flags, api); await api[action](platform); }); } -function addSmokeCommand(program: Command, getApi: () => Promise): void { +function addSmokeCommand(program: Command, getApi: GetApi): void { const command = program .command('smoke') .description('Launch the app, traverse every story, optionally run authored tests, and shut the app down.') @@ -185,12 +184,12 @@ function addSmokeCommand(program: Command, getApi: () => Promise { - const api = await getApi(); + const api = await getApi(flags); await api.smoke(resolvePlatform(flags, api), { mode: flags.mode }); }); } -function addServerCommand(program: Command, getApi: () => Promise): void { +function addServerCommand(program: Command, getApi: GetApi): void { const command = program .command('server') .description('Start the Storybook channel and MCP server.') @@ -198,7 +197,7 @@ function addServerCommand(program: Command, getApi: () => Promise', 'server port; defaults to STORYBOOK_WS_PORT or 7007', parsePort); addPlatformOptions(command); command.action(async (flags: ServerFlags) => { - const api = await getApi(); + const api = await getApi(flags); await api.server(resolvePlatform(flags, api), { host: flags.host, port: flags.port, @@ -208,9 +207,10 @@ function addServerCommand(program: Command, getApi: () => Promise { bundleIdentifier: instance.bundleIdentifier, leasePath: path.join(storybookRoot, 'storybook-desktop.generated', 'application-lease.macos.json'), }); + expect(macosManifest.renderer).toBe('fabric'); + }); + + test('records macOS Paper without changing the endpoint or native identity', () => { + const config = makeDesktopStorybookConfig({ projectRoot: storybookRoot }); + const instance = createDesktopStorybookInstance({ + bundleIdentifierPrefix: config.macosBundleIdentifier, + projectRoot: config.projectRoot, + }); + const manifest = createDesktopStorybookDriverManifest({ + config, + instance, + nativeDriver, + platform: 'macos', + renderer: 'paper', + storyManifest: { ...storyManifest, endpoint: 'macos' }, + }); + + expect(manifest).toMatchObject({ + application: { bundleIdentifier: instance.bundleIdentifier }, + endpoint: 'macos', + renderer: 'paper', + targetId: 'agenticstorybook-macos', + }); }); test('removes a stale application lease before writing a new manifest', () => { diff --git a/packages/agentic/storybook-desktop/src/driver/driverManifest.ts b/packages/agentic/storybook-desktop/src/driver/driverManifest.ts index 0f7b2af9dd..efc6034582 100644 --- a/packages/agentic/storybook-desktop/src/driver/driverManifest.ts +++ b/packages/agentic/storybook-desktop/src/driver/driverManifest.ts @@ -39,6 +39,7 @@ export type CreateDesktopStorybookDriverManifestOptions = { instance: DesktopStorybookInstance; nativeDriver: NativeDriverArtifact; platform: Platforms; + renderer?: DesktopStorybookDriverManifest['renderer']; storyManifest: DesktopStoryManifest; }; @@ -48,6 +49,7 @@ export function createDesktopStorybookDriverManifest({ instance, nativeDriver, platform, + renderer = platform === 'win32' ? 'paper' : 'fabric', storyManifest, }: CreateDesktopStorybookDriverManifestOptions): DesktopStorybookDriverManifest { const nativeOptions = config.getNativeDriverOptions(platform); @@ -72,7 +74,7 @@ export function createDesktopStorybookDriverManifest({ nativeDriver, platformManifestDigest: storyManifest.platformManifestDigest, portablePlanDigest: storyManifest.portablePlanDigest, - renderer: platform === 'win32' ? 'paper' : 'fabric', + renderer, schemaVersion: 2, storyManifest, storybookPort: instance.storybookPort, From 2ba40f41994ec20dc9f07b04e07d1d6c0b8d3414 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Fri, 2 Oct 2026 18:19:10 -0700 Subject: [PATCH 7/9] Constrain LayoutStableText labels to their reserved width Fix the macOS Paper CI geometry failure without relaxing its native alignment assertions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/macos-paper-storybook.md | 2 ++ .../button/__snapshots__/button.test.tsx.snap | 4 ++++ .../primitives/layout-stable-text/CONTRACT.md | 4 +++- .../layout-stable-text.test.tsx | 22 ++++++++++++++++++- .../layout-stable-text/layout-stable-text.tsx | 1 + 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/.changeset/macos-paper-storybook.md b/.changeset/macos-paper-storybook.md index 0f1683eff8..1696a2ec35 100644 --- a/.changeset/macos-paper-storybook.md +++ b/.changeset/macos-paper-storybook.md @@ -6,3 +6,5 @@ Add a macOS-only `--paper` option to prepare, build, launch, and smoke test Storybook with Paper while preserving Fabric as the default renderer. Use centered alignment in macOS Paper CompoundItemLayout stories to avoid the native Yoga baseline crash without changing the primitive or Fabric demonstrations. + +Constrain LayoutStableText's visible label to the reserved width so native Paper text measurement cannot overflow its layout reservation. diff --git a/packages/agentic/components/src/components/button/__snapshots__/button.test.tsx.snap b/packages/agentic/components/src/components/button/__snapshots__/button.test.tsx.snap index 99fa028ba6..92570980ea 100644 --- a/packages/agentic/components/src/components/button/__snapshots__/button.test.tsx.snap +++ b/packages/agentic/components/src/components/button/__snapshots__/button.test.tsx.snap @@ -647,6 +647,7 @@ exports[`Button matches the selected visual state snapshot across appearances 1` "fontSize": 14, "fontWeight": "600", "lineHeight": 20, + "maxWidth": "100%", "textAlign": "center", }, ], @@ -687,6 +688,7 @@ exports[`Button matches the selected visual state snapshot across appearances 1` "fontSize": 14, "fontWeight": "600", "lineHeight": 20, + "maxWidth": "100%", "textAlign": "center", }, ], @@ -727,6 +729,7 @@ exports[`Button matches the selected visual state snapshot across appearances 1` "fontSize": 14, "fontWeight": "600", "lineHeight": 20, + "maxWidth": "100%", "textAlign": "center", }, ], @@ -767,6 +770,7 @@ exports[`Button matches the selected visual state snapshot across appearances 1` "fontSize": 14, "fontWeight": "600", "lineHeight": 20, + "maxWidth": "100%", "textAlign": "center", }, ], diff --git a/packages/agentic/components/src/primitives/layout-stable-text/CONTRACT.md b/packages/agentic/components/src/primitives/layout-stable-text/CONTRACT.md index e943816488..98d16d5fd5 100644 --- a/packages/agentic/components/src/primitives/layout-stable-text/CONTRACT.md +++ b/packages/agentic/components/src/primitives/layout-stable-text/CONTRACT.md @@ -8,7 +8,9 @@ the reserved text. The visible Text retains its own measured line box rather than stretching to the reserve's height. - Both labels use the same available width so wrapping and truncation remain - consistent. The caller supplies a reserve large enough for every visible state. + consistent. The visible label's maximum width is the reserve's width, including + when native text measurement rounds up or the caller supplies a larger width. + The caller supplies a reserve large enough for every visible state. - Consumer text styles are preserved before the primitive's structural styles. - The root remains inaccessible so the visible text supplies semantics. - The overlay does not intercept input intended for the visible Text or its diff --git a/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.test.tsx b/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.test.tsx index a852424366..79a17ad1cd 100644 --- a/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.test.tsx +++ b/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.test.tsx @@ -51,11 +51,31 @@ describe('LayoutStableText', () => { const visible = component.getByTestId('visible'); expect(visible.props.numberOfLines).toBe(2); - expect(StyleSheet.flatten(visible.props.style)).toEqual({ flexShrink: 1, fontSize: 12, lineHeight: 16 }); + expect(StyleSheet.flatten(visible.props.style)).toEqual({ flexShrink: 1, fontSize: 12, lineHeight: 16, maxWidth: '100%' }); expect(StyleSheet.flatten(visible.parent?.props.style)).toMatchObject({ left: 0, right: 0, justifyContent: 'center' }); expect(StyleSheet.flatten(component.getByTestId('stable-text').props.style)).toMatchObject({ width: 80 }); }); + it('bounds visible text by the reserved width even when its native measurement or consumer width is larger', async () => { + const component = await render( + Stable label} + style={{ width: 102 }} + testID="stable-text" + visible={ + + Stable label + + } + />, + ); + const visible = component.getByTestId('visible'); + + expect(StyleSheet.flatten(visible.props.style)).toMatchObject({ flexShrink: 1, maxWidth: '100%', width: 104 }); + expect(StyleSheet.flatten(visible.parent?.props.style)).toMatchObject({ left: 0, right: 0 }); + expect(StyleSheet.flatten(component.getByTestId('stable-text').props.style)).toMatchObject({ width: 102 }); + }); + it('preserves visible text semantics and interaction through the overlay', async () => { const onPress = jest.fn(); const component = await render( diff --git a/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.tsx b/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.tsx index 879a370575..698841041b 100644 --- a/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.tsx +++ b/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.tsx @@ -45,5 +45,6 @@ const styles = StyleSheet.create({ }, text: { flexShrink: 1, + maxWidth: '100%', }, }); From e21d6ceddaffde62406546fd3d610ffeda4ca10d Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Fri, 2 Oct 2026 18:49:38 -0700 Subject: [PATCH 8/9] Measure the native reserved Text instead of its wrapper View Compare Text line boxes with the same Paper pixel-rounding policy, preserve the native assertions, and remove the ineffective max-width production change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/macos-paper-storybook.md | 2 +- .../button/__snapshots__/button.test.tsx.snap | 4 --- .../primitives/layout-stable-text/CONTRACT.md | 4 +-- .../layout-stable-text.stories.tsx | 18 ++++++------- .../layout-stable-text.test.tsx | 25 +++++++++++-------- .../layout-stable-text/layout-stable-text.tsx | 1 - 6 files changed, 24 insertions(+), 30 deletions(-) diff --git a/.changeset/macos-paper-storybook.md b/.changeset/macos-paper-storybook.md index 1696a2ec35..96e394309b 100644 --- a/.changeset/macos-paper-storybook.md +++ b/.changeset/macos-paper-storybook.md @@ -7,4 +7,4 @@ Add a macOS-only `--paper` option to prepare, build, launch, and smoke test Stor Use centered alignment in macOS Paper CompoundItemLayout stories to avoid the native Yoga baseline crash without changing the primitive or Fabric demonstrations. -Constrain LayoutStableText's visible label to the reserved width so native Paper text measurement cannot overflow its layout reservation. +Measure LayoutStableText's hidden reserve Text directly in native alignment stories, avoiding Paper Text-versus-View pixel-rounding mismatches without relaxing the geometry assertions. diff --git a/packages/agentic/components/src/components/button/__snapshots__/button.test.tsx.snap b/packages/agentic/components/src/components/button/__snapshots__/button.test.tsx.snap index 92570980ea..99fa028ba6 100644 --- a/packages/agentic/components/src/components/button/__snapshots__/button.test.tsx.snap +++ b/packages/agentic/components/src/components/button/__snapshots__/button.test.tsx.snap @@ -647,7 +647,6 @@ exports[`Button matches the selected visual state snapshot across appearances 1` "fontSize": 14, "fontWeight": "600", "lineHeight": 20, - "maxWidth": "100%", "textAlign": "center", }, ], @@ -688,7 +687,6 @@ exports[`Button matches the selected visual state snapshot across appearances 1` "fontSize": 14, "fontWeight": "600", "lineHeight": 20, - "maxWidth": "100%", "textAlign": "center", }, ], @@ -729,7 +727,6 @@ exports[`Button matches the selected visual state snapshot across appearances 1` "fontSize": 14, "fontWeight": "600", "lineHeight": 20, - "maxWidth": "100%", "textAlign": "center", }, ], @@ -770,7 +767,6 @@ exports[`Button matches the selected visual state snapshot across appearances 1` "fontSize": 14, "fontWeight": "600", "lineHeight": 20, - "maxWidth": "100%", "textAlign": "center", }, ], diff --git a/packages/agentic/components/src/primitives/layout-stable-text/CONTRACT.md b/packages/agentic/components/src/primitives/layout-stable-text/CONTRACT.md index 98d16d5fd5..e943816488 100644 --- a/packages/agentic/components/src/primitives/layout-stable-text/CONTRACT.md +++ b/packages/agentic/components/src/primitives/layout-stable-text/CONTRACT.md @@ -8,9 +8,7 @@ the reserved text. The visible Text retains its own measured line box rather than stretching to the reserve's height. - Both labels use the same available width so wrapping and truncation remain - consistent. The visible label's maximum width is the reserve's width, including - when native text measurement rounds up or the caller supplies a larger width. - The caller supplies a reserve large enough for every visible state. + consistent. The caller supplies a reserve large enough for every visible state. - Consumer text styles are preserved before the primitive's structural styles. - The root remains inaccessible so the visible text supplies semantics. - The overlay does not intercept input intended for the visible Text or its diff --git a/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.stories.tsx b/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.stories.tsx index 19ead348b2..43ed87a954 100644 --- a/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.stories.tsx +++ b/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.stories.tsx @@ -105,15 +105,13 @@ function AlignmentMeasurement() { const [visible, setVisible] = useState(); return ( - setReserve(event.nativeEvent.layout)} - > + Stable label} + reserve={ + setReserve(event.nativeEvent.layout)}> + Stable label + + } visible={ { const assert: typeof import('node:assert') = (await import('node:assert')).default; if (platform === 'macos') { - // Fabric paragraphs do not expose their testID to AX; use their native onLayout measurements. + // Measure both Text line boxes; Paper rounds Text and wrapper View frames differently. const status = await browser.$('~layout-stable-text-native-metrics'); await browser.waitUntil(async () => (await status.getText()).startsWith('{')); const metrics: { reserve: LayoutRectangle; visible: LayoutRectangle } = JSON.parse(await status.getText()); @@ -178,7 +176,7 @@ export const Overview: Story = { ); assert( Math.abs(metrics.visible.x) <= 1 && metrics.visible.width <= metrics.reserve.width + 1, - 'Visible text must retain leading alignment and fit the reserved width.', + `Visible text must retain leading alignment and fit the reserved width: ${JSON.stringify(metrics)}.`, ); return; } diff --git a/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.test.tsx b/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.test.tsx index 79a17ad1cd..0a9867ff81 100644 --- a/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.test.tsx +++ b/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.test.tsx @@ -51,29 +51,32 @@ describe('LayoutStableText', () => { const visible = component.getByTestId('visible'); expect(visible.props.numberOfLines).toBe(2); - expect(StyleSheet.flatten(visible.props.style)).toEqual({ flexShrink: 1, fontSize: 12, lineHeight: 16, maxWidth: '100%' }); + expect(StyleSheet.flatten(visible.props.style)).toEqual({ flexShrink: 1, fontSize: 12, lineHeight: 16 }); expect(StyleSheet.flatten(visible.parent?.props.style)).toMatchObject({ left: 0, right: 0, justifyContent: 'center' }); expect(StyleSheet.flatten(component.getByTestId('stable-text').props.style)).toMatchObject({ width: 80 }); }); - it('bounds visible text by the reserved width even when its native measurement or consumer width is larger', async () => { + it('preserves native layout measurements for both the hidden reserve and visible text', async () => { + const onReserveLayout = jest.fn(); + const onVisibleLayout = jest.fn(); const component = await render( Stable label} - style={{ width: 102 }} - testID="stable-text" + reserve={Reserve} visible={ - - Stable label + + Visible } />, ); - const visible = component.getByTestId('visible'); + const reserveLayout = { nativeEvent: { layout: { x: 0, y: 0, width: 103, height: 24 } } }; + const visibleLayout = { nativeEvent: { layout: { x: 0, y: 4, width: 104, height: 16 } } }; + + await fireEvent(component.getByText('Reserve', { includeHiddenElements: true }), 'layout', reserveLayout); + await fireEvent(component.getByTestId('visible'), 'layout', visibleLayout); - expect(StyleSheet.flatten(visible.props.style)).toMatchObject({ flexShrink: 1, maxWidth: '100%', width: 104 }); - expect(StyleSheet.flatten(visible.parent?.props.style)).toMatchObject({ left: 0, right: 0 }); - expect(StyleSheet.flatten(component.getByTestId('stable-text').props.style)).toMatchObject({ width: 102 }); + expect(onReserveLayout).toHaveBeenCalledWith(reserveLayout); + expect(onVisibleLayout).toHaveBeenCalledWith(visibleLayout); }); it('preserves visible text semantics and interaction through the overlay', async () => { diff --git a/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.tsx b/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.tsx index 698841041b..879a370575 100644 --- a/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.tsx +++ b/packages/agentic/components/src/primitives/layout-stable-text/layout-stable-text.tsx @@ -45,6 +45,5 @@ const styles = StyleSheet.create({ }, text: { flexShrink: 1, - maxWidth: '100%', }, }); From 21f75691f28354b57fac78664507179b96d127a0 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Fri, 2 Oct 2026 19:14:35 -0700 Subject: [PATCH 9/9] Deduplicate compatible Storybook runtime dependencies Align locked React, theming, and UI-common packages after the main-branch UI upgrades while preserving strict Metro duplicate checks and existing dependency ranges. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/macos-paper-storybook.md | 2 + apps/storybook/README.md | 5 ++ yarn.lock | 80 ++--------------------------- 3 files changed, 10 insertions(+), 77 deletions(-) diff --git a/.changeset/macos-paper-storybook.md b/.changeset/macos-paper-storybook.md index 96e394309b..88af0bff13 100644 --- a/.changeset/macos-paper-storybook.md +++ b/.changeset/macos-paper-storybook.md @@ -8,3 +8,5 @@ Add a macOS-only `--paper` option to prepare, build, launch, and smoke test Stor Use centered alignment in macOS Paper CompoundItemLayout stories to avoid the native Yoga baseline crash without changing the primitive or Fabric demonstrations. Measure LayoutStableText's hidden reserve Text directly in native alignment stories, avoiding Paper Text-versus-View pixel-rounding mismatches without relaxing the geometry assertions. + +Deduplicate compatible Storybook React, theming, and shared UI dependencies in the lockfile after desktop UI updates, keeping strict Metro duplicate checks enabled. diff --git a/apps/storybook/README.md b/apps/storybook/README.md index 3bcb906e90..336aa29d10 100644 --- a/apps/storybook/README.md +++ b/apps/storybook/README.md @@ -188,6 +188,11 @@ them. Win32 continues to exercise Callout through its Paper endpoint. Storybook's development bundle intentionally contains separate `pretty-format` and `react-is` versions used by its internal tooling. They are excluded from the duplicate-module enforcement; React, React Native, and application dependencies remain checked. +After upgrading Storybook UI or addon packages, run +`yarn dedupe '@storybook/*' storybook --strategy highest` from the repository +root to align compatible locked React, theming, and shared UI dependencies. +Keep these runtime packages covered by Metro's duplicate checks rather than +adding exclusions for mismatched versions. ## Running on Win32 diff --git a/yarn.lock b/yarn.lock index fd6e7252c8..01cc006503 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9989,24 +9989,6 @@ __metadata: languageName: node linkType: hard -"@storybook/react-dom-shim@npm:10.5.6": - version: 10.5.6 - resolution: "@storybook/react-dom-shim@npm:10.5.6" - peerDependencies: - "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - "@types/react-dom": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.5.6 - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 10c0/d6393cf9532fb7f2dddedeeac6bef35d9abcf3bfa48c1d8bf5a1be4d2167746d252f638e15feb6a24f1a4b21d51d4df1870e70acfb1e1d663aedada0cf9f1c4d - languageName: node - linkType: hard - "@storybook/react-dom-shim@npm:10.6.0": version: 10.6.0 resolution: "@storybook/react-dom-shim@npm:10.6.0" @@ -10025,19 +10007,7 @@ __metadata: languageName: node linkType: hard -"@storybook/react-native-theming@npm:^10.4.7, @storybook/react-native-theming@npm:^10.5.4": - version: 10.5.4 - resolution: "@storybook/react-native-theming@npm:10.5.4" - dependencies: - polished: "npm:^4.3.1" - peerDependencies: - react: "*" - react-native: ">=0.57.0" - checksum: 10c0/da1d153288315406885a647d6397fb02166e2cb621f059aabed37b8dd439d6a6ec5bf9ba3aa4779e9b5fa03c209aff64d6af6bcc4771a21278a2aeaeac0c2f04 - languageName: node - linkType: hard - -"@storybook/react-native-theming@npm:^10.6.0": +"@storybook/react-native-theming@npm:^10.4.7, @storybook/react-native-theming@npm:^10.5.4, @storybook/react-native-theming@npm:^10.6.0": version: 10.6.0 resolution: "@storybook/react-native-theming@npm:10.6.0" dependencies: @@ -10049,25 +10019,7 @@ __metadata: languageName: node linkType: hard -"@storybook/react-native-ui-common@npm:^10.4.7, @storybook/react-native-ui-common@npm:^10.5.4": - version: 10.5.4 - resolution: "@storybook/react-native-ui-common@npm:10.5.4" - dependencies: - "@nozbe/microfuzz": "npm:^1.0.0" - "@storybook/react": "npm:^10.5.4" - "@storybook/react-native-theming": "npm:^10.5.4" - es-toolkit: "npm:^1.49.0" - memoizerific: "npm:^1.11.3" - ts-dedent: "npm:^2.3.0" - peerDependencies: - react: "*" - react-native: ">=0.57.0" - storybook: ^10.5.4 - checksum: 10c0/ba33fee75b282eeded4116651e88ac0d59ee0f49207f2d6f4a7a994b9a2023d6ea41303b6708c37a48cd9a13e1064eb08b8b182817f0cbc9b6ed0a71367ca8dc - languageName: node - linkType: hard - -"@storybook/react-native-ui-common@npm:^10.6.0": +"@storybook/react-native-ui-common@npm:^10.4.7, @storybook/react-native-ui-common@npm:^10.5.4, @storybook/react-native-ui-common@npm:^10.6.0": version: 10.6.0 resolution: "@storybook/react-native-ui-common@npm:10.6.0" dependencies: @@ -10170,33 +10122,7 @@ __metadata: languageName: node linkType: hard -"@storybook/react@npm:^10.5.4": - version: 10.5.6 - resolution: "@storybook/react@npm:10.5.6" - dependencies: - "@storybook/global": "npm:^5.0.0" - "@storybook/react-dom-shim": "npm:10.5.6" - react-docgen: "npm:^8.0.2" - react-docgen-typescript: "npm:^2.2.2" - peerDependencies: - "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - "@types/react-dom": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.5.6 - typescript: ">= 4.9.x" - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - typescript: - optional: true - checksum: 10c0/74eff8c1b3a52235a62a43d0e1646ef8f9f557d31745d9a313d735da98d1800950a76d16544b54d6756c237df573f0e4303af2197efc0c141f29e1b4e67399f3 - languageName: node - linkType: hard - -"@storybook/react@npm:^10.6.0": +"@storybook/react@npm:^10.5.4, @storybook/react@npm:^10.6.0": version: 10.6.0 resolution: "@storybook/react@npm:10.6.0" dependencies: