diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 000000000..183ea93ab
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,3 @@
+* @kommander @msmps @Hona
+/packages/react/ @msmps @Adictya @fezproof @kommander @Hona
+/packages/solid/ @Adictya @fezproof @msmps @kommander @Hona
diff --git a/.github/workflows/build-core.yml b/.github/workflows/build-core.yml
new file mode 100644
index 000000000..d0b5704fe
--- /dev/null
+++ b/.github/workflows/build-core.yml
@@ -0,0 +1,37 @@
+name: Build Core
+
+on:
+ push:
+ pull_request:
+ branches: [main]
+
+jobs:
+ build:
+ name: Core - Build and Test
+ runs-on: macos-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Setup Zig
+ uses: goto-bus-stop/setup-zig@v2
+ with:
+ version: 0.15.2
+
+ - name: Install dependencies
+ run: bun install
+
+ - name: Build
+ run: |
+ cd packages/core
+ bun run build
+
+ - name: Run tests
+ run: |
+ cd packages/core
+ bun run test
diff --git a/.github/workflows/build-examples.yml b/.github/workflows/build-examples.yml
new file mode 100644
index 000000000..e0511e532
--- /dev/null
+++ b/.github/workflows/build-examples.yml
@@ -0,0 +1,218 @@
+name: Build Examples
+
+on:
+ workflow_call:
+ inputs:
+ version:
+ description: "Version being released"
+ required: true
+ type: string
+ isDryRun:
+ description: "Whether this is a dry run release"
+ required: false
+ type: boolean
+ default: false
+
+jobs:
+ build-examples:
+ name: Build Example Executables
+ runs-on: macos-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Install dependencies FIRST (before copying built packages)
+ run: |
+ echo "Installing base dependencies..."
+ bun install
+
+ echo ""
+ echo "✅ Base dependencies installed"
+ echo "Note: bun-webgpu will be installed by the build script (src/examples/build.ts) for all platforms"
+
+ - name: Download npm packages artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: npm-packages-${{ inputs.version }}
+
+ - name: Extract and install npm packages
+ run: |
+ set -e
+ echo "Extracting npm packages..."
+ test -f npm-packages.zip || (echo "❌ npm-packages.zip not found!" && exit 1)
+ unzip -q npm-packages.zip
+
+ # Create node_modules structure
+ mkdir -p node_modules/@opentui
+ mkdir -p packages/core/node_modules/@opentui
+
+ # Copy core dist (required)
+ echo "Copying core dist..."
+ test -d "npm-packages/core-dist" || (echo "❌ core-dist not found in artifact!" && exit 1)
+ cp -r npm-packages/core-dist packages/core/dist
+ test -f packages/core/dist/package.json || (echo "❌ core dist/package.json missing after copy!" && exit 1)
+ echo "✅ Core dist copied"
+
+ # Copy native packages to core's node_modules (required - these OVERRIDE the npm versions)
+ echo "Copying built native packages (overriding any npm versions)..."
+ test -d "npm-packages/core-native-packages" || (echo "❌ core-native-packages not found in artifact!" && exit 1)
+ cp -r npm-packages/core-native-packages/* packages/core/node_modules/@opentui/
+ test -n "$(ls -A packages/core/node_modules/@opentui/)" || (echo "❌ No native packages copied!" && exit 1)
+ echo "✅ Native packages copied"
+
+ # Copy other package dists (optional)
+ if [ -d "npm-packages/react-dist" ]; then
+ cp -r npm-packages/react-dist packages/react/dist
+ echo "✅ React dist copied"
+ fi
+
+ if [ -d "npm-packages/solid-dist" ]; then
+ cp -r npm-packages/solid-dist packages/solid/dist
+ echo "✅ Solid dist copied"
+ fi
+
+ echo ""
+ echo "Package extraction complete:"
+ ls -lah packages/core/dist/
+ ls -lah packages/core/node_modules/@opentui/
+
+ - name: Verify bun-webgpu is available for build
+ run: |
+ echo "Checking bun-webgpu installation..."
+
+ # Check root node_modules first
+ if [ -d node_modules/bun-webgpu ]; then
+ echo "✅ bun-webgpu found in root node_modules"
+ # Check packages/core/node_modules
+ elif [ -d packages/core/node_modules/bun-webgpu ]; then
+ echo "✅ bun-webgpu found in packages/core/node_modules"
+ else
+ echo "❌ bun-webgpu not found in root or packages/core/node_modules!"
+ echo "Note: The build script will install it, but it should already be present"
+ exit 1
+ fi
+
+ # Check for platform-specific packages in .bun cache (where bun install --os="*" --cpu="*" puts them)
+ echo ""
+ echo "Checking for platform-specific bun-webgpu packages in cache..."
+ if [ -d node_modules/.bun/bun-webgpu-darwin-arm64@0.1.4 ]; then
+ echo "✅ bun-webgpu-darwin-arm64 found in cache"
+ else
+ echo "⚠️ bun-webgpu-darwin-arm64 not found (will be installed during build)"
+ fi
+
+ if [ -d node_modules/.bun/bun-webgpu-darwin-x64@0.1.4 ]; then
+ echo "✅ bun-webgpu-darwin-x64 found in cache"
+ else
+ echo "⚠️ bun-webgpu-darwin-x64 not found (will be installed during build)"
+ fi
+
+ if [ -d node_modules/.bun/bun-webgpu-linux-x64@0.1.4 ]; then
+ echo "✅ bun-webgpu-linux-x64 found in cache"
+ else
+ echo "⚠️ bun-webgpu-linux-x64 not found (will be installed during build)"
+ fi
+
+ if [ -d node_modules/.bun/bun-webgpu-win32-x64@0.1.4 ]; then
+ echo "✅ bun-webgpu-win32-x64 found in cache"
+ else
+ echo "⚠️ bun-webgpu-win32-x64 not found (will be installed during build)"
+ fi
+
+ echo ""
+ echo "✅ bun-webgpu ready for bundling"
+
+ - name: Build examples
+ run: |
+ set -e
+ cd packages/core
+ bun src/examples/build.ts
+
+ - name: Verify example builds
+ run: |
+ set -e
+ echo "Verifying example executables..."
+ test -f "packages/core/src/examples/dist/darwin-x64/opentui-examples" || (echo "❌ darwin-x64 example missing!" && exit 1)
+ test -f "packages/core/src/examples/dist/darwin-arm64/opentui-examples" || (echo "❌ darwin-arm64 example missing!" && exit 1)
+ test -f "packages/core/src/examples/dist/linux-x64/opentui-examples" || (echo "❌ linux-x64 example missing!" && exit 1)
+ test -f "packages/core/src/examples/dist/windows-x64/opentui-examples.exe" || (echo "❌ windows-x64 example missing!" && exit 1)
+
+ echo "✅ All example executables verified"
+ echo ""
+ ls -lah packages/core/src/examples/dist/*/
+
+ # Create separate zips for each platform's examples
+ - name: Package examples - darwin-x64
+ run: |
+ set -e
+ mkdir -p artifacts/examples-darwin-x64
+ cp packages/core/src/examples/dist/darwin-x64/opentui-examples artifacts/examples-darwin-x64/
+ cd artifacts
+ zip -r examples-darwin-x64.zip examples-darwin-x64/
+ test -f examples-darwin-x64.zip || (echo "❌ Failed to create darwin-x64 zip" && exit 1)
+ test -s examples-darwin-x64.zip || (echo "❌ darwin-x64 zip is empty" && exit 1)
+ echo "✅ darwin-x64 packaged successfully ($(du -h examples-darwin-x64.zip | cut -f1))"
+
+ - name: Package examples - darwin-arm64
+ run: |
+ set -e
+ mkdir -p artifacts/examples-darwin-arm64
+ cp packages/core/src/examples/dist/darwin-arm64/opentui-examples artifacts/examples-darwin-arm64/
+ cd artifacts
+ zip -r examples-darwin-arm64.zip examples-darwin-arm64/
+ test -f examples-darwin-arm64.zip || (echo "❌ Failed to create darwin-arm64 zip" && exit 1)
+ test -s examples-darwin-arm64.zip || (echo "❌ darwin-arm64 zip is empty" && exit 1)
+ echo "✅ darwin-arm64 packaged successfully ($(du -h examples-darwin-arm64.zip | cut -f1))"
+
+ - name: Package examples - linux-x64
+ run: |
+ set -e
+ mkdir -p artifacts/examples-linux-x64
+ cp packages/core/src/examples/dist/linux-x64/opentui-examples artifacts/examples-linux-x64/
+ cd artifacts
+ zip -r examples-linux-x64.zip examples-linux-x64/
+ test -f examples-linux-x64.zip || (echo "❌ Failed to create linux-x64 zip" && exit 1)
+ test -s examples-linux-x64.zip || (echo "❌ linux-x64 zip is empty" && exit 1)
+ echo "✅ linux-x64 packaged successfully ($(du -h examples-linux-x64.zip | cut -f1))"
+
+ - name: Package examples - windows-x64
+ run: |
+ set -e
+ mkdir -p artifacts/examples-windows-x64
+ cp packages/core/src/examples/dist/windows-x64/opentui-examples.exe artifacts/examples-windows-x64/
+ cd artifacts
+ zip -r examples-windows-x64.zip examples-windows-x64/
+ test -f examples-windows-x64.zip || (echo "❌ Failed to create windows-x64 zip" && exit 1)
+ test -s examples-windows-x64.zip || (echo "❌ windows-x64 zip is empty" && exit 1)
+ echo "✅ windows-x64 packaged successfully ($(du -h examples-windows-x64.zip | cut -f1))"
+
+ - name: Verify all artifacts before upload
+ run: |
+ set -e
+ echo "Verifying all artifacts exist..."
+ test -f artifacts/examples-darwin-x64.zip || (echo "❌ examples-darwin-x64.zip missing!" && exit 1)
+ test -f artifacts/examples-darwin-arm64.zip || (echo "❌ examples-darwin-arm64.zip missing!" && exit 1)
+ test -f artifacts/examples-linux-x64.zip || (echo "❌ examples-linux-x64.zip missing!" && exit 1)
+ test -f artifacts/examples-windows-x64.zip || (echo "❌ examples-windows-x64.zip missing!" && exit 1)
+
+ echo ""
+ echo "✅ All artifacts verified. Ready to upload:"
+ ls -lah artifacts/*.zip
+
+ - name: Upload example executables
+ uses: actions/upload-artifact@v4
+ with:
+ name: example-executables-${{ inputs.version }}
+ path: |
+ artifacts/examples-darwin-x64.zip
+ artifacts/examples-darwin-arm64.zip
+ artifacts/examples-linux-x64.zip
+ artifacts/examples-windows-x64.zip
+ if-no-files-found: error
+ retention-days: 30
diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml
new file mode 100644
index 000000000..bc63283b4
--- /dev/null
+++ b/.github/workflows/build-native.yml
@@ -0,0 +1,210 @@
+name: Build Native
+
+on:
+ workflow_call:
+ inputs:
+ version:
+ description: "Version being released"
+ required: true
+ type: string
+ isDryRun:
+ description: "Whether this is a dry run release"
+ required: false
+ type: boolean
+ default: false
+
+env:
+ ZIG_VERSION: 0.15.2
+
+jobs:
+ build-native:
+ name: Build Native Libraries
+ runs-on: macos-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Setup Zig
+ uses: goto-bus-stop/setup-zig@v2
+ with:
+ version: ${{ env.ZIG_VERSION }}
+
+ - name: Install dependencies
+ run: bun install
+
+ - name: Build packages (cross-compile for all platforms)
+ run: |
+ cd packages/core
+ bun run build:native --all
+ bun run build:lib
+
+ - name: Verify build outputs
+ run: |
+ set -e
+ echo "Checking for native binary packages..."
+ ls -lah packages/core/node_modules/@opentui/
+ echo "Checking for dist packages..."
+ ls -lah packages/*/dist/
+
+ # Verify critical files exist (all 6 platforms)
+ echo ""
+ echo "Verifying native binaries exist..."
+ test -f packages/core/node_modules/@opentui/core-darwin-x64/libopentui.dylib || (echo "❌ darwin-x64 binary missing!" && exit 1)
+ test -f packages/core/node_modules/@opentui/core-darwin-arm64/libopentui.dylib || (echo "❌ darwin-arm64 binary missing!" && exit 1)
+ test -f packages/core/node_modules/@opentui/core-linux-x64/libopentui.so || (echo "❌ linux-x64 binary missing!" && exit 1)
+ test -f packages/core/node_modules/@opentui/core-linux-arm64/libopentui.so || (echo "❌ linux-arm64 binary missing!" && exit 1)
+ test -f packages/core/node_modules/@opentui/core-win32-x64/opentui.dll || (echo "❌ windows-x64 binary missing!" && exit 1)
+ test -f packages/core/node_modules/@opentui/core-win32-arm64/opentui.dll || (echo "❌ windows-arm64 binary missing!" && exit 1)
+
+ echo "Verifying dist packages exist..."
+ test -d packages/core/dist || (echo "❌ core dist missing!" && exit 1)
+ test -f packages/core/dist/package.json || (echo "❌ core dist/package.json missing!" && exit 1)
+
+ echo "✅ All required build outputs verified (6 platforms)"
+
+ # Create separate zips for each platform's native binaries
+ - name: Package native binaries - darwin-x64
+ run: |
+ set -e
+ mkdir -p artifacts/native-darwin-x64
+ cp packages/core/node_modules/@opentui/core-darwin-x64/libopentui.dylib artifacts/native-darwin-x64/
+ cd artifacts
+ zip -r native-darwin-x64.zip native-darwin-x64/
+ test -f native-darwin-x64.zip || (echo "❌ Failed to create darwin-x64 zip" && exit 1)
+ test -s native-darwin-x64.zip || (echo "❌ darwin-x64 zip is empty" && exit 1)
+ echo "✅ darwin-x64 packaged successfully ($(du -h native-darwin-x64.zip | cut -f1))"
+
+ - name: Package native binaries - darwin-arm64
+ run: |
+ set -e
+ mkdir -p artifacts/native-darwin-arm64
+ cp packages/core/node_modules/@opentui/core-darwin-arm64/libopentui.dylib artifacts/native-darwin-arm64/
+ cd artifacts
+ zip -r native-darwin-arm64.zip native-darwin-arm64/
+ test -f native-darwin-arm64.zip || (echo "❌ Failed to create darwin-arm64 zip" && exit 1)
+ test -s native-darwin-arm64.zip || (echo "❌ darwin-arm64 zip is empty" && exit 1)
+ echo "✅ darwin-arm64 packaged successfully ($(du -h native-darwin-arm64.zip | cut -f1))"
+
+ - name: Package native binaries - linux-x64
+ run: |
+ set -e
+ mkdir -p artifacts/native-linux-x64
+ cp packages/core/node_modules/@opentui/core-linux-x64/libopentui.so artifacts/native-linux-x64/
+ cd artifacts
+ zip -r native-linux-x64.zip native-linux-x64/
+ test -f native-linux-x64.zip || (echo "❌ Failed to create linux-x64 zip" && exit 1)
+ test -s native-linux-x64.zip || (echo "❌ linux-x64 zip is empty" && exit 1)
+ echo "✅ linux-x64 packaged successfully ($(du -h native-linux-x64.zip | cut -f1))"
+
+ - name: Package native binaries - windows-x64
+ run: |
+ set -e
+ mkdir -p artifacts/native-windows-x64
+ cp packages/core/node_modules/@opentui/core-win32-x64/opentui.dll artifacts/native-windows-x64/
+ cd artifacts
+ zip -r native-windows-x64.zip native-windows-x64/
+ test -f native-windows-x64.zip || (echo "❌ Failed to create windows-x64 zip" && exit 1)
+ test -s native-windows-x64.zip || (echo "❌ windows-x64 zip is empty" && exit 1)
+ echo "✅ windows-x64 packaged successfully ($(du -h native-windows-x64.zip | cut -f1))"
+
+ - name: Package native binaries - linux-arm64
+ run: |
+ set -e
+ mkdir -p artifacts/native-linux-arm64
+ cp packages/core/node_modules/@opentui/core-linux-arm64/libopentui.so artifacts/native-linux-arm64/
+ cd artifacts
+ zip -r native-linux-arm64.zip native-linux-arm64/
+ test -f native-linux-arm64.zip || (echo "❌ Failed to create linux-arm64 zip" && exit 1)
+ test -s native-linux-arm64.zip || (echo "❌ linux-arm64 zip is empty" && exit 1)
+ echo "✅ linux-arm64 packaged successfully ($(du -h native-linux-arm64.zip | cut -f1))"
+
+ - name: Package native binaries - windows-arm64
+ run: |
+ set -e
+ mkdir -p artifacts/native-windows-arm64
+ cp packages/core/node_modules/@opentui/core-win32-arm64/opentui.dll artifacts/native-windows-arm64/
+ cd artifacts
+ zip -r native-windows-arm64.zip native-windows-arm64/
+ test -f native-windows-arm64.zip || (echo "❌ Failed to create windows-arm64 zip" && exit 1)
+ test -s native-windows-arm64.zip || (echo "❌ windows-arm64 zip is empty" && exit 1)
+ echo "✅ windows-arm64 packaged successfully ($(du -h native-windows-arm64.zip | cut -f1))"
+
+ # Package the built npm packages (for use by build-examples and npm-publish)
+ - name: Package npm packages
+ run: |
+ set -e
+ mkdir -p artifacts/npm-packages
+
+ # Copy core package dist and native packages
+ echo "Packaging core dist..."
+ cp -r packages/core/dist artifacts/npm-packages/core-dist
+ test -f artifacts/npm-packages/core-dist/package.json || (echo "❌ core dist/package.json missing after copy!" && exit 1)
+
+ echo "Packaging core native packages..."
+ cp -r packages/core/node_modules/@opentui artifacts/npm-packages/core-native-packages
+
+ # Copy other package dists (optional - don't fail if missing)
+ echo "Packaging other packages..."
+ if [ -d packages/react/dist ]; then
+ cp -r packages/react/dist artifacts/npm-packages/react-dist
+ echo "✅ React dist packaged"
+ else
+ echo "⚠️ React dist not found (skipping)"
+ fi
+
+ if [ -d packages/solid/dist ]; then
+ cp -r packages/solid/dist artifacts/npm-packages/solid-dist
+ echo "✅ Solid dist packaged"
+ else
+ echo "⚠️ Solid dist not found (skipping)"
+ fi
+
+ cd artifacts
+ zip -r npm-packages.zip npm-packages/
+ test -f npm-packages.zip || (echo "❌ Failed to create npm-packages zip" && exit 1)
+ test -s npm-packages.zip || (echo "❌ npm-packages zip is empty" && exit 1)
+ echo "✅ npm packages packaged successfully ($(du -h npm-packages.zip | cut -f1))"
+
+ - name: Verify all artifacts before upload
+ run: |
+ set -e
+ echo "Verifying all artifacts exist..."
+ test -f artifacts/native-darwin-x64.zip || (echo "❌ native-darwin-x64.zip missing!" && exit 1)
+ test -f artifacts/native-darwin-arm64.zip || (echo "❌ native-darwin-arm64.zip missing!" && exit 1)
+ test -f artifacts/native-linux-x64.zip || (echo "❌ native-linux-x64.zip missing!" && exit 1)
+ test -f artifacts/native-linux-arm64.zip || (echo "❌ native-linux-arm64.zip missing!" && exit 1)
+ test -f artifacts/native-windows-x64.zip || (echo "❌ native-windows-x64.zip missing!" && exit 1)
+ test -f artifacts/native-windows-arm64.zip || (echo "❌ native-windows-arm64.zip missing!" && exit 1)
+ test -f artifacts/npm-packages.zip || (echo "❌ npm-packages.zip missing!" && exit 1)
+
+ echo ""
+ echo "✅ All 6 platform artifacts verified. Ready to upload:"
+ ls -lah artifacts/*.zip
+
+ - name: Upload native binaries artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: native-binaries-${{ inputs.version }}
+ path: |
+ artifacts/native-darwin-x64.zip
+ artifacts/native-darwin-arm64.zip
+ artifacts/native-linux-x64.zip
+ artifacts/native-linux-arm64.zip
+ artifacts/native-windows-x64.zip
+ artifacts/native-windows-arm64.zip
+ if-no-files-found: error
+ retention-days: 30
+
+ - name: Upload npm packages artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: npm-packages-${{ inputs.version }}
+ path: artifacts/npm-packages.zip
+ if-no-files-found: error
+ retention-days: 30
diff --git a/.github/workflows/build-react.yml b/.github/workflows/build-react.yml
new file mode 100644
index 000000000..d2931a54d
--- /dev/null
+++ b/.github/workflows/build-react.yml
@@ -0,0 +1,42 @@
+name: Build React
+
+on:
+ push:
+ pull_request:
+ branches: [main]
+
+jobs:
+ build:
+ name: React - Build and Test
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Setup Zig
+ uses: goto-bus-stop/setup-zig@v2
+ with:
+ version: 0.15.2
+
+ - name: Install dependencies
+ run: bun install
+
+ - name: Build core
+ run: |
+ cd packages/core
+ bun run build
+
+ - name: Build
+ run: |
+ cd packages/react
+ bun run build --ci
+
+ - name: Run tests
+ run: |
+ cd packages/react
+ bun run test
diff --git a/.github/workflows/build-solid.yml b/.github/workflows/build-solid.yml
new file mode 100644
index 000000000..3748262b3
--- /dev/null
+++ b/.github/workflows/build-solid.yml
@@ -0,0 +1,42 @@
+name: Build Solid
+
+on:
+ push:
+ pull_request:
+ branches: [main]
+
+jobs:
+ build:
+ name: Solid - Build and Test
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Setup Zig
+ uses: goto-bus-stop/setup-zig@v2
+ with:
+ version: 0.15.2
+
+ - name: Install dependencies
+ run: bun install
+
+ - name: Build core
+ run: |
+ cd packages/core
+ bun run build
+
+ - name: Build
+ run: |
+ cd packages/solid
+ bun run build --ci
+
+ - name: Run tests
+ run: |
+ cd packages/solid
+ bun run test
diff --git a/.github/workflows/npm-latest-release.yml b/.github/workflows/npm-latest-release.yml
new file mode 100644
index 000000000..427f75b42
--- /dev/null
+++ b/.github/workflows/npm-latest-release.yml
@@ -0,0 +1,129 @@
+name: NPM Publish
+
+on:
+ workflow_call:
+ inputs:
+ version:
+ description: "Version being published"
+ required: true
+ type: string
+ isDryRun:
+ description: "Whether this is a dry run (npm publish --dry-run)"
+ required: false
+ type: boolean
+ default: false
+ secrets:
+ NPM_TOKEN:
+ required: true
+
+permissions:
+ contents: write
+ pull-requests: write
+
+jobs:
+ publish:
+ runs-on: macos-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Install dependencies
+ run: bun install
+
+ - name: Download npm packages artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: npm-packages-${{ inputs.version }}
+
+ - name: Extract npm packages
+ run: |
+ echo "Extracting npm packages..."
+ unzip -q npm-packages.zip
+
+ # Copy core dist
+ if [ -d "npm-packages/core-dist" ]; then
+ cp -r npm-packages/core-dist packages/core/dist
+ echo "Copied core dist"
+ fi
+
+ # Copy native packages to core's node_modules
+ if [ -d "npm-packages/core-native-packages" ]; then
+ mkdir -p packages/core/node_modules/@opentui
+ cp -r npm-packages/core-native-packages/* packages/core/node_modules/@opentui/
+ echo "Copied native packages"
+ fi
+
+ # Copy other package dists
+ if [ -d "npm-packages/react-dist" ]; then
+ cp -r npm-packages/react-dist packages/react/dist
+ echo "Copied react dist"
+ fi
+
+ if [ -d "npm-packages/solid-dist" ]; then
+ cp -r npm-packages/solid-dist packages/solid/dist
+ echo "Copied solid dist"
+ fi
+
+ echo "Package extraction complete"
+
+ - name: Publish packages (dry-run)
+ if: ${{ inputs.isDryRun }}
+ run: |
+ echo "=========================================="
+ echo "DRY RUN MODE - Not publishing to npm"
+ echo "=========================================="
+ echo ""
+ echo "Skipping pre-publish validation (npm version check would fail for dry-runs)"
+ echo ""
+
+ # Show what would be published (just check if packages can be packed)
+ echo "Packages that would be published:"
+ echo ""
+
+ echo "📦 Checking @opentui/core..."
+ cd packages/core/dist && npm pack --dry-run
+
+ echo ""
+ echo "📦 Checking @opentui/react..."
+ cd ../../react/dist && npm pack --dry-run
+
+ echo ""
+ echo "📦 Checking @opentui/solid..."
+ cd ../../solid/dist && npm pack --dry-run
+
+ echo ""
+ echo "=========================================="
+ echo "DRY RUN COMPLETE - All packages validated"
+ echo "=========================================="
+ env:
+ NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+ CI: true
+
+ - name: Publish packages (production)
+ if: ${{ !inputs.isDryRun }}
+ run: |
+ echo "=========================================="
+ echo "PRODUCTION MODE - Publishing to npm"
+ echo "=========================================="
+
+ # Run pre-publish validation
+ bun run pre-publish
+
+ # Publish packages
+ bun run publish
+
+ echo ""
+ echo "=========================================="
+ echo "PUBLISH COMPLETE"
+ echo "=========================================="
+ env:
+ NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+ CI: true
diff --git a/.github/workflows/npm-release.yml b/.github/workflows/npm-release.yml
new file mode 100644
index 000000000..5c0b35b41
--- /dev/null
+++ b/.github/workflows/npm-release.yml
@@ -0,0 +1,67 @@
+name: NPM Snapshot Release
+
+on:
+ push:
+ tags:
+ - "*snapshot*"
+
+permissions:
+ contents: write
+ pull-requests: write
+
+jobs:
+ release:
+ runs-on: macos-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Setup Zig
+ uses: goto-bus-stop/setup-zig@v2
+ with:
+ version: 0.15.2
+
+ - name: Extract version from tag
+ id: extract_version
+ run: |
+ TAG=${GITHUB_REF#refs/tags/v}
+ echo "version=$TAG" >> $GITHUB_OUTPUT
+ echo "Extracted version: $TAG"
+
+ - name: Generate snapshot version
+ id: version_scheme
+ run: |
+ TAG_VERSION="${{ steps.extract_version.outputs.version }}"
+ COMMIT_SHA=$(git rev-parse --short=8 HEAD)
+
+ # Pre-release snapshot
+ VERSION="0.0.0-$(date +%Y%m%d)-${COMMIT_SHA}"
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ echo "Using snapshot version: $VERSION"
+
+ - name: Install dependencies
+ run: bun i
+
+ - name: Prepare release versions
+ run: bun run prepare-release "${{ steps.version_scheme.outputs.version }}"
+
+ - name: Build packages (cross-compile for all platforms)
+ run: |
+ cd packages/core
+ bun run build:native --all
+ bun run build:lib
+ cd ../solid && bun run build
+ cd ../react && bun run build
+
+ - name: Publish packages
+ run: bun run publish
+ env:
+ NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+ CI: true
diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml
new file mode 100644
index 000000000..0837554bc
--- /dev/null
+++ b/.github/workflows/opencode.yml
@@ -0,0 +1,27 @@
+name: opencode
+
+on:
+ issue_comment:
+ types: [created]
+
+jobs:
+ opencode:
+ if: |
+ contains(github.event.comment.body, ' /oc') ||
+ startsWith(github.event.comment.body, '/oc') ||
+ contains(github.event.comment.body, ' /opencode') ||
+ startsWith(github.event.comment.body, '/opencode')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Run opencode
+ uses: sst/opencode/github@latest
+ env:
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
+ with:
+ model: anthropic/claude-sonnet-4-20250514
diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml
new file mode 100644
index 000000000..d86fc7406
--- /dev/null
+++ b/.github/workflows/prettier.yml
@@ -0,0 +1,32 @@
+name: Format
+
+on:
+ pull_request:
+ branches: [main]
+
+jobs:
+ prettier-core:
+ name: Prettier Check
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Check Prettier formatting
+ run: |
+ PRETTIER_VERSION=$(bun -e 'console.log(require("./package.json").devDependencies.prettier)')
+ bunx prettier@$PRETTIER_VERSION --check --log-level warn .
+
+ - name: Show formatting differences
+ if: failure()
+ run: |
+ echo "=== Showing differences for each file ==="
+ PRETTIER_VERSION=$(bun -e 'console.log(require("./package.json").devDependencies.prettier)')
+ bunx prettier@$PRETTIER_VERSION --write .
+ git diff --color=always
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 000000000..3b7e10cf5
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,233 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - "v*"
+
+permissions:
+ contents: write
+ pull-requests: write
+
+jobs:
+ # Extract version and check if it's a dry run
+ prepare:
+ name: Prepare Release
+ runs-on: ubuntu-latest
+ outputs:
+ version: ${{ steps.extract.outputs.version }}
+ fullTag: ${{ steps.extract.outputs.fullTag }}
+ isDryRun: ${{ steps.extract.outputs.isDryRun }}
+ releaseTitle: ${{ steps.extract.outputs.releaseTitle }}
+
+ steps:
+ - name: Extract version and dry-run flag
+ id: extract
+ run: |
+ TAG=${GITHUB_REF#refs/tags/v}
+ echo "Full tag: $TAG"
+
+ # Check if this is a dry run (contains -dry.)
+ if [[ "$TAG" =~ -dry\. ]]; then
+ IS_DRY_RUN=true
+ # Extract base version (remove -dry.X suffix for validation)
+ VERSION=$(echo "$TAG" | sed -E 's/-dry\.[0-9]+$//')
+ RELEASE_TITLE="Release v$TAG (DRY RUN)"
+ echo "This is a DRY RUN release"
+ else
+ IS_DRY_RUN=false
+ VERSION="$TAG"
+ RELEASE_TITLE="Release v$TAG"
+ echo "This is a PRODUCTION release"
+ fi
+
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ echo "fullTag=$TAG" >> $GITHUB_OUTPUT
+ echo "isDryRun=$IS_DRY_RUN" >> $GITHUB_OUTPUT
+ echo "releaseTitle=$RELEASE_TITLE" >> $GITHUB_OUTPUT
+
+ echo "Version (for validation): $VERSION"
+ echo "Full Tag (for artifacts): $TAG"
+ echo "Is Dry Run: $IS_DRY_RUN"
+ echo "Release Title: $RELEASE_TITLE"
+
+ # Version validation check
+ validate-version:
+ name: Validate Version
+ needs: prepare
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Check version matches package.json files
+ run: |
+ TAG_VERSION="${{ needs.prepare.outputs.version }}"
+ echo "Validating version: $TAG_VERSION"
+
+ # Check packages/*/package.json versions
+ FAILED=false
+ for pkg in packages/*/; do
+ if [ -f "$pkg/package.json" ]; then
+ PKG_VERSION=$(node -p "require('./$pkg/package.json').version")
+ if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
+ echo "❌ Package $pkg version ($PKG_VERSION) does not match tag version ($TAG_VERSION)"
+ FAILED=true
+ else
+ echo "✅ Package $pkg version matches: $PKG_VERSION"
+ fi
+ fi
+ done
+
+ if [ "$FAILED" = true ]; then
+ echo ""
+ echo "Version validation FAILED!"
+ echo "Please update package.json versions to match the tag version: $TAG_VERSION"
+ exit 1
+ fi
+
+ echo ""
+ echo "✅ All package versions match tag version: $TAG_VERSION"
+
+ # Build native libraries and packages
+ build-native:
+ name: Build Native
+ needs: [prepare, validate-version]
+ uses: ./.github/workflows/build-native.yml
+ with:
+ version: ${{ needs.prepare.outputs.fullTag }}
+ isDryRun: ${{ needs.prepare.outputs.isDryRun == 'true' }}
+
+ # Build example executables
+ build-examples:
+ name: Build Examples
+ needs: [prepare, validate-version, build-native]
+ uses: ./.github/workflows/build-examples.yml
+ with:
+ version: ${{ needs.prepare.outputs.fullTag }}
+ isDryRun: ${{ needs.prepare.outputs.isDryRun == 'true' }}
+
+ # Publish to npm
+ npm-publish:
+ name: NPM Publish
+ needs: [prepare, validate-version, build-native]
+ uses: ./.github/workflows/npm-latest-release.yml
+ secrets:
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+ with:
+ version: ${{ needs.prepare.outputs.fullTag }}
+ isDryRun: ${{ needs.prepare.outputs.isDryRun == 'true' }}
+
+ # Create GitHub release with assets
+ github-release:
+ name: Create GitHub Release
+ needs: [prepare, build-native, build-examples, npm-publish]
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Download all artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: artifacts
+
+ - name: Organize release assets
+ run: |
+ set -e
+ WORK_DIR=$(pwd)
+ mkdir -p "$WORK_DIR/release-assets"
+
+ FULL_TAG="${{ needs.prepare.outputs.fullTag }}"
+ echo "Organizing release assets for tag: $FULL_TAG"
+ echo "Working directory: $WORK_DIR"
+
+ # Verify artifact directories exist
+ test -d "artifacts/native-binaries-$FULL_TAG" || (echo "❌ native-binaries artifact directory not found!" && exit 1)
+ test -d "artifacts/example-executables-$FULL_TAG" || (echo "❌ example-executables artifact directory not found!" && exit 1)
+
+ # Copy native binaries (unzip and rezip with versioned names)
+ echo "Processing native binaries..."
+ cd "artifacts/native-binaries-$FULL_TAG"
+
+ for zip_file in native-*.zip; do
+ if [ -f "$zip_file" ]; then
+ platform=$(echo "$zip_file" | sed 's/native-//' | sed 's/.zip//')
+ echo " Processing $platform..."
+ unzip -q "$zip_file"
+
+ # Repackage with versioned name
+ dir_name=$(echo "$zip_file" | sed 's/.zip//')
+ if [ -d "$dir_name" ]; then
+ cd "$dir_name"
+ zip -r "$WORK_DIR/release-assets/opentui-native-v${FULL_TAG}-${platform}.zip" .
+ cd ..
+
+ # Verify the output
+ test -f "$WORK_DIR/release-assets/opentui-native-v${FULL_TAG}-${platform}.zip" || (echo "❌ Failed to create native $platform release asset!" && exit 1)
+ echo " ✅ opentui-native-v${FULL_TAG}-${platform}.zip created"
+ else
+ echo "❌ Directory $dir_name not found after unzip!" && exit 1
+ fi
+ fi
+ done
+
+ cd "$WORK_DIR"
+
+ # Copy example executables (unzip and rezip with versioned names)
+ echo "Processing example executables..."
+ cd "artifacts/example-executables-$FULL_TAG"
+
+ for zip_file in examples-*.zip; do
+ if [ -f "$zip_file" ]; then
+ platform=$(echo "$zip_file" | sed 's/examples-//' | sed 's/.zip//')
+ echo " Processing $platform..."
+ unzip -q "$zip_file"
+
+ # Repackage with versioned name
+ dir_name=$(echo "$zip_file" | sed 's/.zip//')
+ if [ -d "$dir_name" ]; then
+ cd "$dir_name"
+ zip -r "$WORK_DIR/release-assets/opentui-examples-v${FULL_TAG}-${platform}.zip" .
+ cd ..
+
+ # Verify the output
+ test -f "$WORK_DIR/release-assets/opentui-examples-v${FULL_TAG}-${platform}.zip" || (echo "❌ Failed to create examples $platform release asset!" && exit 1)
+ echo " ✅ opentui-examples-v${FULL_TAG}-${platform}.zip created"
+ else
+ echo "❌ Directory $dir_name not found after unzip!" && exit 1
+ fi
+ fi
+ done
+
+ cd "$WORK_DIR"
+
+ # Verify all expected release assets exist
+ echo ""
+ echo "Verifying all release assets..."
+ EXPECTED_ASSETS=10 # 6 native (darwin-x64, darwin-arm64, linux-x64, linux-arm64, windows-x64, windows-arm64) + 4 examples (no linux-arm64, no windows-arm64)
+ ACTUAL_ASSETS=$(ls -1 release-assets/*.zip 2>/dev/null | wc -l | tr -d ' ')
+
+ if [ "$ACTUAL_ASSETS" -ne "$EXPECTED_ASSETS" ]; then
+ echo "❌ Expected $EXPECTED_ASSETS release assets, found $ACTUAL_ASSETS"
+ ls -lah release-assets/ || echo "No release assets found"
+ exit 1
+ fi
+
+ echo "✅ All $ACTUAL_ASSETS release assets prepared:"
+ echo " - 6 native binaries (all platforms)"
+ echo " - 4 example executables (darwin-x64, darwin-arm64, linux-x64, windows-x64)"
+ ls -lah release-assets/
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ name: ${{ needs.prepare.outputs.releaseTitle }}
+ files: release-assets/*.zip
+ generate_release_notes: true
+ draft: false
+ prerelease: ${{ needs.prepare.outputs.isDryRun == 'true' }}
+ token: ${{ secrets.GITHUB_TOKEN }}
+ fail_on_unmatched_files: true
diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml
new file mode 100644
index 000000000..5c6712cd7
--- /dev/null
+++ b/.github/workflows/review.yml
@@ -0,0 +1,78 @@
+name: Guidelines Check
+
+on:
+ issue_comment:
+ types: [created]
+
+jobs:
+ check-guidelines:
+ if: |
+ github.event.issue.pull_request &&
+ startsWith(github.event.comment.body, '/review') &&
+ contains(fromJson('["OWNER","MEMBER"]'), github.event.comment.author_association)
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: write
+ steps:
+ - name: Get PR number
+ id: pr-number
+ run: |
+ if [ "${{ github.event_name }}" = "pull_request_target" ]; then
+ echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
+ else
+ echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 1
+
+ - name: Install opencode
+ run: curl -fsSL https://opencode.ai/install | bash
+
+ - name: Get PR details
+ id: pr-details
+ run: |
+ gh api /repos/${{ github.repository }}/pulls/${{ steps.pr-number.outputs.number }} > pr_data.json
+ echo "title=$(jq -r .title pr_data.json)" >> $GITHUB_OUTPUT
+ echo "sha=$(jq -r .head.sha pr_data.json)" >> $GITHUB_OUTPUT
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Check PR guidelines compliance
+ env:
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "gh pr review*": "deny", "*": "deny" } }'
+ run: |
+ PR_BODY=$(jq -r .body pr_data.json)
+ opencode run -m anthropic/claude-sonnet-4-5 "A new pull request has been created: '${{ steps.pr-details.outputs.title }}'
+
+
+ ${{ steps.pr-number.outputs.number }}
+
+
+
+ $PR_BODY
+
+
+ Please check all the code changes in this pull request against the style guide, also look for any bugs if they exist. Diffs are important but make sure you read the entire file to get proper context. Make it clear the suggestions are merely suggestions and the human can decide what to do
+
+ When critiquing code against the style guide, be sure that the code is ACTUALLY in violation, don't complain about else statements if they already use early returns there. You may complain about excessive nesting though, regardless of else statement usage.
+ When critiquing code style don't be a zealot, we don't like "let" statements but sometimes they are the simpliest option, if someone does a bunch of nesting with let, they should consider using iife (see packages/opencode/src/util.iife.ts)
+
+ Use the gh cli to create comments on the files for the violations. Try to leave the comment on the exact line number. If you have a suggested fix include it in a suggestion code block.
+
+ Command MUST be like this.
+ \`\`\`
+ gh api \
+ --method POST \
+ -H \"Accept: application/vnd.github+json\" \
+ -H \"X-GitHub-Api-Version: 2022-11-28\" \
+ /repos/${{ github.repository }}/pulls/${{ steps.pr-number.outputs.number }}/comments \
+ -f 'body=[summary of issue]' -f 'commit_id=${{ steps.pr-details.outputs.sha }}' -f 'path=[path-to-file]' -F \"line=[line]\" -f 'side=RIGHT'
+ \`\`\`
+
+ Only create comments for actual violations. If the code follows all guidelines, comment 'lgtm' AND NOTHING ELSE!!!!."
diff --git a/.gitignore b/.gitignore
index bf0c3d33a..a82151d07 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,15 @@
# dependencies (bun install)
node_modules
+Session.vim
+
# output
out
dist
+packed
*.tgz
*.log
+*.txt
# code coverage
coverage
@@ -34,9 +38,4 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Finder (MacOS) folder config
.DS_Store
-# Zig build cache
-.zig-cache
-zig-out
-src/zig/lib
-
-screenshot-*.png
\ No newline at end of file
+screenshot-*.png
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 000000000..c87cd9554
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,2 @@
+**/.zig-cache/**
+packages/core/src/lib/tree-sitter/default-parsers.ts
diff --git a/.zig-version b/.zig-version
new file mode 100644
index 000000000..4312e0d0c
--- /dev/null
+++ b/.zig-version
@@ -0,0 +1 @@
+0.15.2
diff --git a/AGENTS.md b/AGENTS.md
index cd9ee6cbb..b567119b2 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -4,11 +4,13 @@ Default to using Bun instead of Node.js.
- Use `bun ` instead of `node ` or `ts-node `
- Use `bun test` instead of `jest` or `vitest`
-- Use `bun build ` instead of `webpack` or `esbuild`
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
- Use `bun run