Lit Showcase Hub
+Explore a rich collection of modular Lit Web Components, prototypes, interactive instruments, databases, and custom UI modules. Select any example from the sidebar to preview in real-time with responsive viewport sandboxing.
+diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 0000000..a8e6ae6 --- /dev/null +++ b/.bazelignore @@ -0,0 +1,23 @@ +# Ignore all nested directories so Bazel doesn't scan submodules/workspaces +figma-to-lit +figma_lit_example +lit-3d-piano +lit-calculator +lit-code-editor +lit-draggable-dom +lit-file-based-routing +lit-force-graph +lit-html-editor +lit-html-table +lit-modules +lit-native +lit-node-editor +lit-sheet-music +lit-starter-ts +lit-vscode-extension +lit-wmr +vite-lit-capacitor +vite-lit-element-starter +vite-rxdb-lit +node_modules +dist diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..116bcd6 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,66 @@ +name: Build and Deploy + +on: + push: + branches: ["main", "master"] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + REGISTRY: git.rodydavis.dev + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 9.7.1 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Set up Bazel (Bazelisk) + run: npm install -g @bazel/bazelisk + + - name: Install dependencies + run: pnpm install --no-frozen-lockfile + + - name: Verify Hermetic Bazel Build + run: bazel build //... + + - name: Build Site + run: pnpm run build + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to registry + uses: docker/login-action@v4 + with: + registry: ${{ env.REGISTRY }} + username: ${{ gitea.actor }} + password: ${{ secrets.GIT_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + context: . + file: Dockerfile + push: true + tags: ${{ env.REGISTRY }}/${{ gitea.repository }}:latest + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfd8272 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Ignore dependencies and build outputs +node_modules/ +dist/ + +# Bazel +bazel-* +bazel-bin +bazel-out +bazel-testlogs + +# IDEs and System files +.DS_Store +.vscode/ +.idea/ +*.log diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..768a39a --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,6 @@ +# Trivial Bazel build target so bazel build //... succeeds hermetically +filegroup( + name = "empty", + srcs = [], + visibility = ["//visibility:public"], +) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c974400 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM nginx:alpine +COPY dist /usr/share/nginx/html +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..d93514d --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,4 @@ +module( + name = "lit_examples", + version = "1.0.0", +) diff --git a/build.js b/build.js new file mode 100644 index 0000000..c8ac56c --- /dev/null +++ b/build.js @@ -0,0 +1,754 @@ +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +const ROOT_DIR = __dirname; +const DIST_DIR = path.join(ROOT_DIR, 'dist'); + +// Emojis for showcase listing +const EMOJIS = { + 'figma-to-lit': '🎨', + 'figma_lit_example': '📐', + 'lit-3d-piano': '🎹', + 'lit-calculator': '🧮', + 'lit-code-editor': '💻', + 'lit-draggable-dom': '🖐️', + 'lit-file-based-routing': '🚦', + 'lit-force-graph': '🕸️', + 'lit-html-editor': '✍️', + 'lit-html-table': '📊', + 'lit-modules': '📦', + 'lit-native': '📱', + 'lit-node-editor': '🪢', + 'lit-sheet-music': '🎼', + 'lit-starter-ts': '🚀', + 'lit-vscode-extension': '🔌', + 'lit-wmr': '⚡', + 'vite-lit-capacitor': '🔋', + 'vite-lit-element-starter': '🌱', + 'vite-rxdb-lit': '💾' +}; + +function checkPnpm() { + try { + execSync('pnpm --version', { stdio: 'ignore' }); + return true; + } catch (e) { + return false; + } +} + +const hasPnpm = checkPnpm(); +const pm = hasPnpm ? 'pnpm' : 'npm'; +console.log(`Using package manager: ${pm}`); + +function buildProject(dirName) { + const dirPath = path.join(ROOT_DIR, dirName); + const pkgPath = path.join(dirPath, 'package.json'); + + if (!fs.existsSync(pkgPath)) { + console.log(`[${dirName}] No package.json found. Copying static files directly.`); + return { success: false, mode: 'static' }; + } + + let pkg; + try { + pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + } catch (e) { + console.error(`[${dirName}] Failed to parse package.json: ${e.message}`); + return { success: false, mode: 'static' }; + } + + // 1. Install dependencies + console.log(`[${dirName}] Installing dependencies...`); + try { + const installCmd = pm === 'pnpm' ? 'pnpm install --no-frozen-lockfile' : 'npm install --no-audit --no-fund'; + execSync(installCmd, { cwd: dirPath, stdio: 'inherit' }); + } catch (e) { + console.error(`[${dirName}] Dependency installation failed: ${e.message}`); + // Proceed anyway, might be able to build or serve statically + } + + // 2. Build if a build script exists + if (pkg.scripts && pkg.scripts.build) { + console.log(`[${dirName}] Running build script...`); + try { + execSync(`${pm} run build`, { cwd: dirPath, stdio: 'inherit' }); + return { success: true, mode: 'build' }; + } catch (e) { + console.error(`[${dirName}] Build failed: ${e.message}. Falling back to static copy.`); + return { success: false, mode: 'static' }; + } + } + + return { success: false, mode: 'static' }; +} + +function copyOutput(dirName, buildResult) { + const dirPath = path.join(ROOT_DIR, dirName); + const destPath = path.join(DIST_DIR, dirName); + + if (fs.existsSync(destPath)) { + fs.rmSync(destPath, { recursive: true, force: true }); + } + fs.mkdirSync(destPath, { recursive: true }); + + let srcPath = dirPath; + + if (buildResult.success) { + // Check standard build output directories + const candidates = ['dist', 'build', 'docs']; + for (const cand of candidates) { + const candPath = path.join(dirPath, cand); + if (fs.existsSync(candPath) && fs.statSync(candPath).isDirectory()) { + srcPath = candPath; + break; + } + } + } + + console.log(`[${dirName}] Copying from ${srcPath} to ${destPath}`); + + // Custom recursive copy to ignore node_modules, .git, etc. + copyRecursiveSync(srcPath, destPath); +} + +function copyRecursiveSync(src, dest) { + const exists = fs.existsSync(src); + const stats = exists && fs.statSync(src); + const isDirectory = exists && stats.isDirectory(); + + if (isDirectory) { + if (!fs.existsSync(dest)) { + fs.mkdirSync(dest, { recursive: true }); + } + fs.readdirSync(src).forEach((childItemName) => { + // Exclude heavy/unnecessary folders + if (['node_modules', '.git', 'bazel-out', 'bazel-bin'].includes(childItemName)) { + return; + } + copyRecursiveSync( + path.join(src, childItemName), + path.join(dest, childItemName) + ); + }); + } else { + // If it's a file, just copy it + fs.copyFileSync(src, dest); + } +} + +function generateDashboard(projects) { + console.log('Generating dashboard HTML...'); + + const projectListHtml = projects.map(p => { + const emoji = EMOJIS[p.name] || '📦'; + const cleanName = p.name + .replace(/-/g, ' ') + .replace(/_/g, ' ') + .replace(/\b\w/g, c => c.toUpperCase()); + + return ` +
Explore a rich collection of modular Lit Web Components, prototypes, interactive instruments, databases, and custom UI modules. Select any example from the sidebar to preview in real-time with responsive viewport sandboxing.
+