diff --git a/.github/workflows/open-vsx.yml b/.github/workflows/open-vsx.yml new file mode 100644 index 0000000..18c3994 --- /dev/null +++ b/.github/workflows/open-vsx.yml @@ -0,0 +1,72 @@ +name: Publish Open VSX + +on: + workflow_dispatch: + inputs: + tag: + description: Existing XBridge GitHub release tag (for example, v1.4.2) + required: true + type: string + +permissions: + contents: read + +jobs: + publish: + name: Publish release VSIX to Open VSX + runs-on: ubuntu-latest + env: + RELEASE_TAG: ${{ inputs.tag }} + + steps: + - name: Set up Node.js + uses: actions/setup-node@v5 + with: + node-version: 22 + + - name: Check configuration and release tag + env: + OVSX_PAT: ${{ secrets.OVSX_PAT }} + run: | + if [ -z "$OVSX_PAT" ]; then + echo "::error::Add an Open VSX access token as the OVSX_PAT repository secret." + exit 1 + fi + if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::Use an existing XBridge release tag such as v1.4.2." + exit 1 + fi + + - name: Download and verify the GitHub release VSIX + env: + GH_TOKEN: ${{ github.token }} + run: | + VERSION="${RELEASE_TAG#v}" + PACKAGE="builds/xbridge-${VERSION}.vsix" + mkdir -p builds + gh release download "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --pattern "xbridge-${VERSION}.vsix" --dir builds + test -s "$PACKAGE" + IDENTITY=$(unzip -p "$PACKAGE" extension/package.json | node -e 'let data = ""; process.stdin.on("data", chunk => data += chunk).on("end", () => { const manifest = JSON.parse(data); console.log(`${manifest.publisher}.${manifest.name}@${manifest.version}`); })') + if [ "$IDENTITY" != "chaiwithcode.xbridge@$VERSION" ]; then + echo "::error::The release VSIX identity does not match chaiwithcode.xbridge@$VERSION." + exit 1 + fi + + - name: Create the namespace if needed + env: + OVSX_PAT: ${{ secrets.OVSX_PAT }} + run: | + HTTP_STATUS=$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' https://open-vsx.org/api/chaiwithcode) + case "$HTTP_STATUS" in + 200) echo "The chaiwithcode namespace already exists." ;; + 404) npx --yes ovsx@1.1.1 create-namespace chaiwithcode ;; + *) echo "::error::Open VSX namespace lookup returned HTTP $HTTP_STATUS."; exit 1 ;; + esac + + - name: Publish to Open VSX + env: + OVSX_PAT: ${{ secrets.OVSX_PAT }} + run: | + VERSION="${RELEASE_TAG#v}" + npx --yes ovsx@1.1.1 publish "builds/xbridge-${VERSION}.vsix" --skip-duplicate + echo "[Check the Open VSX listing](https://open-vsx.org/extension/chaiwithcode/xbridge)" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a5d4069..320d6a2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,6 +54,6 @@ jobs: - name: Publish to Open VSX if: env.OVSX_PAT != '' - run: npx ovsx publish builds/*.vsix --skip-duplicate + run: npx --yes ovsx@1.1.1 publish builds/*.vsix --skip-duplicate env: OVSX_PAT: ${{ secrets.OVSX_PAT }} diff --git a/.vscodeignore b/.vscodeignore index aa1677c..8fa4baf 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -13,3 +13,5 @@ esbuild.js Makefile **/*.tsbuildinfo node_modules/** +builds/** +examples/** diff --git a/CHANGELOG.md b/CHANGELOG.md index 8832d65..7560ecc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 1.4.2 + +- Replaced retired Visual Studio Marketplace metadata badges with supported release and install badges. +- Added a direct Marketplace installation link and extension identifier to the README. +- Excluded local build outputs from published VSIX packages. + ## 1.4.1 - Replaced the generic crossed-tools artwork with a distinctive connected-X brand mark. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c0f69f..d2bbb7d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -99,6 +99,8 @@ git switch -c feature/my-feature Open the pull request against `develop`. When preparing a release, create `release/` from `develop`, complete final verification, and open a pull request into `main`. After release, merge `main` back into `develop` so both branches contain the release commit and tag. +Maintainers preparing the second registry listing can follow the [Open VSX publishing guide](docs/open-vsx.md). + --- ## Submitting Pull Requests diff --git a/README.md b/README.md index bc814f5..afb04c6 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@

CI Status - Marketplace Version - Installs + Marketplace Version + Install XBridge from the Visual Studio Marketplace License

@@ -17,6 +17,8 @@

+ Installation • + Demo • Features • AI Tools • Physical Devices • @@ -27,6 +29,22 @@ --- +## Installation + +Install [XBridge from the Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=chaiwithcode.xbridge), or open the Extensions view in VS Code and search for `chaiwithcode.xbridge`. + +--- + +## See XBridge in action + +![XBridge feature tour showing project controls, passing tests, and an app running in Simulator](docs/images/feature-tour.gif) + +The tour uses the included [XBridgeDemo Xcode project](examples/XBridgeDemo). View the full-resolution captures: [project and destination controls](docs/images/overview.png), [XCTest and Swift Testing in Test Explorer](docs/images/tests.png), [build and run](docs/images/build-and-run.png), and [the iPhone app](docs/images/demo-app.png). + +[Explore every XBridge command and AI tool](docs/capabilities.md). + +--- + ## Why XBridge? - Run the core Xcode workflow from the editor where you write Swift. diff --git a/docs/capabilities.md b/docs/capabilities.md new file mode 100644 index 0000000..0c3be71 --- /dev/null +++ b/docs/capabilities.md @@ -0,0 +1,80 @@ +# XBridge capabilities + +XBridge puts the everyday Xcode workflow in VS Code. These are the commands and tools available in version 1.4.2. Open the Command Palette and type `XBridge:` to find a command; the most common actions also appear in the XBridge sidebar and status bar. + +## Project and workflow + +| Command | What it does | +|:---|:---| +| `XBridge: Select Xcode Project or Workspace` | Choose an `.xcodeproj`, `.xcworkspace`, or Swift package when more than one is available. | +| `XBridge: Select Scheme` | Choose the Xcode scheme. | +| `XBridge: Select Build Configuration` | Switch between configurations such as Debug and Release. | +| `XBridge: Select Scheme, Configuration or Destination` | Open one picker for the active build target. | +| `XBridge: Refresh Schemes` | Reload schemes and targets from Xcode. | +| `XBridge: Open in Xcode` | Open the active project or workspace in Xcode. | +| `XBridge: Getting Started` | Open the guided VS Code walkthrough. | + +## Build, run, test, and packages + +| Command | What it does | +|:---|:---| +| `XBridge: Build` | Compile the active scheme. | +| `XBridge: Build & Run` | Build, install, and launch the app on the selected destination. | +| `XBridge: Stop` | Cancel the active operation or stop the running app. | +| `XBridge: Stop Running App` | Terminate the launched app without shutting down the simulator. | +| `XBridge: Clean Build Folder` | Remove the active project's DerivedData build products. | +| `XBridge: Run Tests` | Run the selected test plan or target with `xcodebuild`. | +| `XBridge: Select Test Plan` | Choose an Xcode test plan, if the scheme has one. | +| `XBridge: Select Test Target` | Limit a run to one test target. | +| `XBridge: Resolve Package Dependencies` | Resolve Swift Package Manager dependencies through Xcode. | + +The native VS Code Test Explorer discovers XCTest methods and Swift Testing `@Test` methods, shows per-test results, and lets you run individual tests. Build errors and test failures appear in Problems and at their source locations. + +## Results and logs + +| Command | What it does | +|:---|:---| +| `XBridge: Show Build Output` | Open the `XBridge Build` output channel. | +| `XBridge: Show Build Issues` | Browse errors and warnings and jump to the source line. | +| `XBridge: Show App Console` | Open the launched app's console output. | + +The status bar also shows the selected scheme and destination, build activity, and error and warning counts. + +## Simulators and devices + +| Command | What it does | +|:---|:---| +| `XBridge: Select Destination (Simulator/Device)` | Choose a simulator or connected physical device. | +| `XBridge: Set as Destination` | Use the selected device from the Destinations tree. | +| `XBridge: Boot Simulator` | Start a simulator. | +| `XBridge: Shutdown Simulator` | Shut down a simulator. | +| `XBridge: Open Simulator.app` | Bring Apple's Simulator app forward. | +| `XBridge: Refresh Simulators` | Reload the local simulator list. | +| `XBridge: Refresh Devices & Simulators` | Reload simulator and connected-device discovery. | +| `XBridge: Erase Simulator Content` | Reset a simulator's content and settings. | +| `XBridge: Copy Simulator UDID` | Copy the simulator identifier. | +| `XBridge: Open URL or Deep Link…` | Open a URL in a simulator. | +| `XBridge: Take Screenshot` | Save a PNG from a booted simulator. | +| `XBridge: Start / Stop Screen Recording` | Save a simulator recording as video. | +| `XBridge: Toggle Light / Dark Appearance` | Change the simulator appearance. | +| `XBridge: Open App Data Container` | Open the installed app's container in Finder. | +| `XBridge: Uninstall App from Simulator` | Remove the installed app. | + +Connected iPhones and iPads appear in Destinations through Apple's `devicectl`. Physical-device builds require working code signing and Developer Mode. + +## AI agent tools + +Compatible VS Code AI agents can call these eight tools directly: + +| Tool | Capability | +|:---|:---| +| `xbridge_build` | Build and return compiler diagnostics. | +| `xbridge_test` | Run tests and return failures. | +| `xbridge_run` | Build and launch an app. | +| `xbridge_listSchemes` | List schemes, configurations, and targets. | +| `xbridge_listSimulators` | List available simulators and status. | +| `xbridge_bootSimulator` | Boot a simulator. | +| `xbridge_readDiagnostics` | Read the last build or test diagnostics. | +| `xbridge_clean` | Clean the Xcode build folder. | + +See the [main README](../README.md#settings) for the eleven configurable settings and [XBridgeDemo](../examples/XBridgeDemo) for a project you can try locally. diff --git a/docs/images/build-and-run.png b/docs/images/build-and-run.png new file mode 100644 index 0000000..2b6772c Binary files /dev/null and b/docs/images/build-and-run.png differ diff --git a/docs/images/demo-app.png b/docs/images/demo-app.png new file mode 100644 index 0000000..af59a8a Binary files /dev/null and b/docs/images/demo-app.png differ diff --git a/docs/images/feature-tour.gif b/docs/images/feature-tour.gif new file mode 100644 index 0000000..7dcf4e0 Binary files /dev/null and b/docs/images/feature-tour.gif differ diff --git a/docs/images/overview.png b/docs/images/overview.png new file mode 100644 index 0000000..cc1011c Binary files /dev/null and b/docs/images/overview.png differ diff --git a/docs/images/tests.png b/docs/images/tests.png new file mode 100644 index 0000000..0de32bd Binary files /dev/null and b/docs/images/tests.png differ diff --git a/docs/open-vsx.md b/docs/open-vsx.md new file mode 100644 index 0000000..541f96c --- /dev/null +++ b/docs/open-vsx.md @@ -0,0 +1,24 @@ +# Publishing XBridge to Open VSX + +Open VSX is a separate registry from the Visual Studio Marketplace. XBridge's extension ID is `chaiwithcode.xbridge` in both registries, but the Marketplace publisher account and token do not authorize Open VSX publishing. + +## One-time publisher setup + +1. Follow the [Open VSX publisher guide](https://github.com/eclipse-openvsx/openvsx/wiki/Publishing-Extensions): sign in to [open-vsx.org](https://open-vsx.org/) with the GitHub account that owns XBridge, connect the matching Eclipse account, and accept the Open VSX Publisher Agreement. +2. Generate an Open VSX access token in your Open VSX account settings. Add it to this repository's GitHub Actions secrets as `OVSX_PAT`. Do not put the token in the repository, a command argument, or an issue. +3. Merge the `Publish Open VSX` workflow into the repository's default branch. In GitHub Actions, run **Publish Open VSX** with an existing GitHub release tag such as `v1.4.2`. The workflow downloads that release's VSIX, verifies its publisher, name, and version, creates the `chaiwithcode` namespace if necessary, and uploads the exact release artifact. Re-running it skips a duplicate version. +4. Inspect the [XBridge Open VSX listing](https://open-vsx.org/extension/chaiwithcode/xbridge), including the icon, description, README, license, and version. If you want the verified-publisher shield, [claim ownership of the namespace](https://github.com/eclipse-openvsx/openvsx/wiki/Namespace-Access); creating the namespace alone does not grant that status. + +The existing `v1.4.2` GitHub release contains `xbridge-1.4.2.vsix`. Its SHA-256 is `aa68331c49396d0014bae908e4348b0995f995b1d9c67e159d5a5f03add700cc`. Use that release artifact for a first `v1.4.2` submission. A new package built from `develop` includes README screenshots that are not yet on the default branch, so those links would remain unavailable until a later release merges the assets into `main`. + +## Future releases + +The tag-triggered [release workflow](../.github/workflows/release.yml) builds one VSIX for the GitHub release, the Visual Studio Marketplace, and Open VSX. When `OVSX_PAT` is configured, its Open VSX step publishes the same VSIX after a `v*` tag is pushed. Each registry accepts a given extension version only once; use a new version for updated contents. + +The [Open VSX CLI](https://github.com/eclipse-openvsx/openvsx/blob/main/cli/README.md) also supports publishing an already packaged VSIX locally with `OVSX_PAT` set in the environment: + +```sh +npx --yes ovsx@1.1.1 publish path/to/xbridge-version.vsix --skip-duplicate +``` + +The manual GitHub workflow is preferred for a release because it uses the exact VSIX already attached to that release. After publication, verify the version through the listing or the [registry API](https://open-vsx.org/api/chaiwithcode/xbridge). diff --git a/examples/XBridgeDemo/.gitignore b/examples/XBridgeDemo/.gitignore new file mode 100644 index 0000000..445b095 --- /dev/null +++ b/examples/XBridgeDemo/.gitignore @@ -0,0 +1,4 @@ +.build/ +.vscode/ +*.xcuserstate +xcuserdata/ diff --git a/examples/XBridgeDemo/README.md b/examples/XBridgeDemo/README.md new file mode 100644 index 0000000..87dcb3e --- /dev/null +++ b/examples/XBridgeDemo/README.md @@ -0,0 +1,9 @@ +# XBridge Demo + +A small, buildable SwiftUI app used to demonstrate XBridge in VS Code. It includes one XCTest and one Swift Testing test so both appear in Test Explorer. + +1. Open this folder in VS Code on a Mac with Xcode installed. +2. Select the `XBridgeDemo` scheme and an iOS Simulator in the XBridge sidebar. +3. Use **Build & Run** to launch the app, or open Test Explorer and run both tests. + +The app has no external packages or signing requirement for Simulator. Xcode build products and local VS Code settings are ignored by Git. diff --git a/examples/XBridgeDemo/XBridgeDemo.xcodeproj/project.pbxproj b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..bb2437b --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/project.pbxproj @@ -0,0 +1,459 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 3339FC342000793B43D0BD6E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D21DF1F5E075569C85A5A80D /* Foundation.framework */; }; + 42D7BFFFA5CBABA2F46D7E50 /* ModernTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABB7018A6B79EA9E51BB8FB /* ModernTests.swift */; }; + 7A85D9FDE9A8B9352A26476B /* XBridgeDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A1711653BCBC7EB5A424C4 /* XBridgeDemoApp.swift */; }; + 7A99EADFB04BDDA6FD8EA929 /* XBridgeDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D0980295DEE023A32B5DC9 /* XBridgeDemoTests.swift */; }; + 9F3DFB026CACCEA2DD8BF023 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C94386DDF7FC57A34733389 /* ContentView.swift */; }; + A056ED2D15E03B6267D834B4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D21DF1F5E075569C85A5A80D /* Foundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 5744DE6251B303F1CB3C9DF3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 926908D120004E2CDF95387F /* Project object */; + proxyType = 1; + remoteGlobalIDString = 38C28A02E02E1A716344BAFD; + remoteInfo = XBridgeDemo; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 15A1711653BCBC7EB5A424C4 /* XBridgeDemoApp.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = XBridgeDemoApp.swift; sourceTree = ""; }; + 1ABB7018A6B79EA9E51BB8FB /* ModernTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ModernTests.swift; sourceTree = ""; }; + 2C5D9B1CEA7CDD627FE83B70 /* XBridgeDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XBridgeDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 6238FA757782EB5EADFF0828 /* XBridgeDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XBridgeDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 89D0980295DEE023A32B5DC9 /* XBridgeDemoTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = XBridgeDemoTests.swift; sourceTree = ""; }; + 8C94386DDF7FC57A34733389 /* ContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + D21DF1F5E075569C85A5A80D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5623CC290D6BB5B3555C9E37 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A056ED2D15E03B6267D834B4 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 797F4E7CC0A84E8ADD920E9C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3339FC342000793B43D0BD6E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4F75EA34634134EB2327C973 /* Products */ = { + isa = PBXGroup; + children = ( + 6238FA757782EB5EADFF0828 /* XBridgeDemo.app */, + 2C5D9B1CEA7CDD627FE83B70 /* XBridgeDemoTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 5AABD117A77BFAA3F72F4001 = { + isa = PBXGroup; + children = ( + 4F75EA34634134EB2327C973 /* Products */, + 76777653C83C84E6DFCCB739 /* Frameworks */, + A4B20FD811B3FAA4740D5F89 /* XBridgeDemo */, + 79D6CE832ACE6D6DC6AC9F05 /* XBridgeDemoTests */, + ); + sourceTree = ""; + }; + 76777653C83C84E6DFCCB739 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C4778F146AEF9B99291A3F90 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 79D6CE832ACE6D6DC6AC9F05 /* XBridgeDemoTests */ = { + isa = PBXGroup; + children = ( + 89D0980295DEE023A32B5DC9 /* XBridgeDemoTests.swift */, + 1ABB7018A6B79EA9E51BB8FB /* ModernTests.swift */, + ); + name = XBridgeDemoTests; + path = XBridgeDemoTests; + sourceTree = ""; + }; + A4B20FD811B3FAA4740D5F89 /* XBridgeDemo */ = { + isa = PBXGroup; + children = ( + 15A1711653BCBC7EB5A424C4 /* XBridgeDemoApp.swift */, + 8C94386DDF7FC57A34733389 /* ContentView.swift */, + ); + name = XBridgeDemo; + path = XBridgeDemo; + sourceTree = ""; + }; + C4778F146AEF9B99291A3F90 /* iOS */ = { + isa = PBXGroup; + children = ( + D21DF1F5E075569C85A5A80D /* Foundation.framework */, + ); + name = iOS; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 38C28A02E02E1A716344BAFD /* XBridgeDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9B8353B98D321DDF7007E5DD /* Build configuration list for PBXNativeTarget "XBridgeDemo" */; + buildPhases = ( + 3ED4D77EFBB1BAF4890B3D9F /* Sources */, + 797F4E7CC0A84E8ADD920E9C /* Frameworks */, + 5A26789469FEB06DBB37C2A2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = XBridgeDemo; + productName = XBridgeDemo; + productReference = 6238FA757782EB5EADFF0828 /* XBridgeDemo.app */; + productType = "com.apple.product-type.application"; + }; + E343816C6E356CED400EAB1F /* XBridgeDemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 974B1B4BE0A6E8DBF5A93B89 /* Build configuration list for PBXNativeTarget "XBridgeDemoTests" */; + buildPhases = ( + 95855EDEB98C88C5E50A19C9 /* Sources */, + 5623CC290D6BB5B3555C9E37 /* Frameworks */, + 56C8A09B750384342F4A89E2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + A3AC2A892FD3D0A005C28BE6 /* PBXTargetDependency */, + ); + name = XBridgeDemoTests; + productName = XBridgeDemoTests; + productReference = 2C5D9B1CEA7CDD627FE83B70 /* XBridgeDemoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 926908D120004E2CDF95387F /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 2660; + LastUpgradeCheck = 2660; + }; + buildConfigurationList = 4FE1DAE27A9BF849A7EFCED6 /* Build configuration list for PBXProject "XBridgeDemo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5AABD117A77BFAA3F72F4001; + minimizedProjectReferenceProxies = 0; + preferredProjectObjectVersion = 77; + productRefGroup = 4F75EA34634134EB2327C973 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 38C28A02E02E1A716344BAFD /* XBridgeDemo */, + E343816C6E356CED400EAB1F /* XBridgeDemoTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 56C8A09B750384342F4A89E2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5A26789469FEB06DBB37C2A2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3ED4D77EFBB1BAF4890B3D9F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A85D9FDE9A8B9352A26476B /* XBridgeDemoApp.swift in Sources */, + 9F3DFB026CACCEA2DD8BF023 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 95855EDEB98C88C5E50A19C9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A99EADFB04BDDA6FD8EA929 /* XBridgeDemoTests.swift in Sources */, + 42D7BFFFA5CBABA2F46D7E50 /* ModernTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + A3AC2A892FD3D0A005C28BE6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = XBridgeDemo; + target = 38C28A02E02E1A716344BAFD /* XBridgeDemo */; + targetProxy = 5744DE6251B303F1CB3C9DF3 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 03F3B0CAB58D500D2D3DCF86 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XBridgeDemo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/XBridgeDemo"; + }; + name = Debug; + }; + 44B5F68238D33181F33D81DC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 5915172CE0D64BA5F77731AE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7E6DCA70F2C65BCFF5E627F8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XBridgeDemo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/XBridgeDemo"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B3BC7A1BB930F45E88B8E6F9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + C0216E6A1D67B593D084CC55 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4FE1DAE27A9BF849A7EFCED6 /* Build configuration list for PBXProject "XBridgeDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B3BC7A1BB930F45E88B8E6F9 /* Debug */, + 44B5F68238D33181F33D81DC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 974B1B4BE0A6E8DBF5A93B89 /* Build configuration list for PBXNativeTarget "XBridgeDemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7E6DCA70F2C65BCFF5E627F8 /* Release */, + 03F3B0CAB58D500D2D3DCF86 /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9B8353B98D321DDF7007E5DD /* Build configuration list for PBXNativeTarget "XBridgeDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C0216E6A1D67B593D084CC55 /* Release */, + 5915172CE0D64BA5F77731AE /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 926908D120004E2CDF95387F /* Project object */; +} diff --git a/examples/XBridgeDemo/XBridgeDemo.xcodeproj/xcshareddata/xcschemes/XBridgeDemo.xcscheme b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/xcshareddata/xcschemes/XBridgeDemo.xcscheme new file mode 100644 index 0000000..be305a4 --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/xcshareddata/xcschemes/XBridgeDemo.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/XBridgeDemo/XBridgeDemo/ContentView.swift b/examples/XBridgeDemo/XBridgeDemo/ContentView.swift new file mode 100644 index 0000000..775e19f --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemo/ContentView.swift @@ -0,0 +1,71 @@ +import SwiftUI + +struct ContentView: View { + private let features = [ + Feature(title: "Build", detail: "Compile with xcodebuild", symbol: "hammer.fill", color: .blue), + Feature(title: "Test", detail: "XCTest & Swift Testing", symbol: "checkmark.seal.fill", color: .green), + Feature(title: "Run", detail: "Simulator or device", symbol: "play.circle.fill", color: .indigo), + Feature(title: "Diagnose", detail: "Inline build issues", symbol: "waveform.path.ecg", color: .orange), + ] + + var body: some View { + NavigationStack { + ScrollView { + VStack(alignment: .leading, spacing: 24) { + VStack(alignment: .leading, spacing: 8) { + Image(systemName: "point.3.connected.trianglepath.dotted") + .font(.system(size: 42, weight: .semibold)) + .foregroundStyle(.blue.gradient) + + Text("Xcode power.\nVS Code flow.") + .font(.system(size: 36, weight: .bold, design: .rounded)) + + Text("A tiny project built to demonstrate XBridge.") + .foregroundStyle(.secondary) + } + + LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 14) { + ForEach(features) { feature in + VStack(alignment: .leading, spacing: 10) { + Image(systemName: feature.symbol) + .font(.title2) + .foregroundStyle(feature.color) + Text(feature.title) + .font(.headline) + Text(feature.detail) + .font(.caption) + .foregroundStyle(.secondary) + } + .frame(maxWidth: .infinity, minHeight: 108, alignment: .leading) + .padding() + .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 18)) + } + } + + Label("Connected to XBridge", systemImage: "checkmark.circle.fill") + .font(.subheadline.weight(.semibold)) + .foregroundStyle(.green) + .frame(maxWidth: .infinity) + .padding() + .background(.green.opacity(0.1), in: Capsule()) + } + .padding(24) + } + .background(Color(.systemGroupedBackground)) + .navigationTitle("XBridge Demo") + } + } +} + +private struct Feature: Identifiable { + let title: String + let detail: String + let symbol: String + let color: Color + + var id: String { title } +} + +#Preview { + ContentView() +} diff --git a/examples/XBridgeDemo/XBridgeDemo/XBridgeDemoApp.swift b/examples/XBridgeDemo/XBridgeDemo/XBridgeDemoApp.swift new file mode 100644 index 0000000..6595fd1 --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemo/XBridgeDemoApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct XBridgeDemoApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/examples/XBridgeDemo/XBridgeDemoTests/ModernTests.swift b/examples/XBridgeDemo/XBridgeDemoTests/ModernTests.swift new file mode 100644 index 0000000..8cd4cae --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemoTests/ModernTests.swift @@ -0,0 +1,9 @@ +import Testing +@testable import XBridgeDemo + +struct ModernTests { + @Test("XBridge discovers Swift Testing tests") + func xbridgeDiscoversModernTests() { + #expect("XBridge".hasPrefix("X")) + } +} diff --git a/examples/XBridgeDemo/XBridgeDemoTests/XBridgeDemoTests.swift b/examples/XBridgeDemo/XBridgeDemoTests/XBridgeDemoTests.swift new file mode 100644 index 0000000..1ce42de --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemoTests/XBridgeDemoTests.swift @@ -0,0 +1,8 @@ +import XCTest +@testable import XBridgeDemo + +final class XBridgeDemoTests: XCTestCase { + func testDemoProjectLoads() { + XCTAssertEqual(2 + 2, 4) + } +} diff --git a/package-lock.json b/package-lock.json index 181bb2e..0235ae5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "xbridge", - "version": "1.4.1", + "version": "1.4.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "xbridge", - "version": "1.4.1", + "version": "1.4.2", "license": "MIT", "devDependencies": { "@types/node": "^20.14.0", diff --git a/package.json b/package.json index 8e76803..7d4bdd6 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "xbridge", "displayName": "XBridge", "description": "Build, test, and run Xcode projects from VS Code—with simulator, device, Test Explorer, and AI agent tools.", - "version": "1.4.1", + "version": "1.4.2", "publisher": "chaiwithcode", "author": { "name": "Deepak Sharma"