diff --git a/.github/scripts/generate-packages.js b/.github/scripts/generate-packages.js new file mode 100644 index 00000000..07e74603 --- /dev/null +++ b/.github/scripts/generate-packages.js @@ -0,0 +1,170 @@ +const fs = require('fs'); +const path = require('path'); + +/** + * Generate packages.json file by scanning the dist directory structure + * @param {string} distPath - Path to the dist directory + * @returns {Object} Generated packages data + */ +function generatePackagesJson(distPath = '.') { + console.log(`Scanning dist directory: ${distPath}`); + + const repositories = []; + + try { + // Read the dist directory + const distDir = fs.readdirSync(distPath, { withFileTypes: true }); + + // Process each subdirectory as a potential repository + for (const dirent of distDir) { + if (dirent.isDirectory() && dirent.name !== '.git') { + console.log(`Processing repository: ${dirent.name}`); + const repoData = scanRepositoryDirectory(path.join(distPath, dirent.name)); + if (repoData) { + repositories.push(repoData); + console.log(` Found ${repoData.releases.length} releases`); + } else { + console.log(` No releases with assets found`); + } + } + } + + // Sort repositories by name + repositories.sort((a, b) => a.name.localeCompare(b.name)); + + // Calculate totals + const totalReleases = repositories.reduce((sum, repo) => sum + repo.releases.length, 0); + const totalAssets = repositories.reduce((sum, repo) => + sum + repo.releases.reduce((releaseSum, release) => releaseSum + release.assetCount, 0), 0 + ); + + const packagesData = { + lastUpdated: new Date().toISOString(), + repositories: repositories, + stats: { + totalRepositories: repositories.length, + totalReleases: totalReleases, + totalAssets: totalAssets + } + }; + + console.log(`Generated packages data:`); + console.log(` Repositories: ${repositories.length}`); + console.log(` Total Releases: ${totalReleases}`); + console.log(` Total Assets: ${totalAssets}`); + + return packagesData; + + } catch (error) { + console.error('Error scanning dist directory:', error); + throw error; + } +} + +/** + * Scan a repository directory to find releases and count assets + * @param {string} repoPath - Path to the repository directory + * @returns {Object|null} Repository data with releases or null if no valid releases + */ +function scanRepositoryDirectory(repoPath) { + const repoName = path.basename(repoPath); + const releases = []; + + try { + if (!fs.existsSync(repoPath) || !fs.statSync(repoPath).isDirectory()) { + return null; + } + + // Scan for release directories + const repoDirContents = fs.readdirSync(repoPath, { withFileTypes: true }); + + for (const dirent of repoDirContents) { + if (dirent.isDirectory()) { + const releaseData = scanReleaseDirectory(path.join(repoPath, dirent.name)); + if (releaseData) { + releases.push(releaseData); + } + } + } + + // Sort releases by tag name (newest first, assuming semantic versioning) + releases.sort((a, b) => b.tag.localeCompare(a.tag, undefined, { numeric: true, sensitivity: 'base' })); + + if (releases.length > 0) { + return { + name: repoName, + releases: releases + }; + } + + return null; + + } catch (error) { + console.error(`Error processing repository ${repoName}:`, error); + return null; + } +} + +/** + * Scan a release directory to count asset files + * @param {string} releasePath - Path to the release directory + * @returns {Object|null} Release data with asset count or null if no assets + */ +function scanReleaseDirectory(releasePath) { + const releaseTag = path.basename(releasePath); + + try { + if (!fs.existsSync(releasePath) || !fs.statSync(releasePath).isDirectory()) { + return null; + } + + // Count actual asset files (exclude hash files and README) + const releaseContents = fs.readdirSync(releasePath, { withFileTypes: true }); + const assetFiles = releaseContents.filter(dirent => { + if (!dirent.isFile()) return false; + + const filename = dirent.name; + // Skip hash files and README + return !(filename.endsWith('.sha256') || + filename.endsWith('.sha512') || + filename.endsWith('.md5') || + filename === 'README.md'); + }); + + if (assetFiles.length > 0) { + return { + tag: releaseTag, + assetCount: assetFiles.length + }; + } + + return null; + + } catch (error) { + console.error(`Error processing release ${releaseTag}:`, error); + return null; + } +} + +/** + * Write packages.json file to the specified path + * @param {Object} packagesData - The packages data to write + * @param {string} outputPath - Path where to write the packages.json file + */ +function writePackagesJson(packagesData, outputPath = './packages.json') { + try { + const jsonString = JSON.stringify(packagesData, null, 2); + fs.writeFileSync(outputPath, jsonString, 'utf8'); + console.log(`Generated packages.json: ${outputPath}`); + } catch (error) { + console.error('Error writing packages.json:', error); + throw error; + } +} + +module.exports = { + generatePackagesJson, + scanRepositoryDirectory, + scanReleaseDirectory, + writePackagesJson +}; diff --git a/.github/workflows/sync-release-assets.yml b/.github/workflows/sync-release-assets.yml index 97f9a383..f2768468 100644 --- a/.github/workflows/sync-release-assets.yml +++ b/.github/workflows/sync-release-assets.yml @@ -67,52 +67,32 @@ jobs: process.chdir('./dist'); await syncReleaseAssets(github, context, isPullRequest, maxNewAssets); - - name: Commit and push changes - if: github.event_name != 'pull_request' - uses: actions-js/push@v1.5 + - name: Generate packages.json + uses: actions/github-script@v8 with: - author_email: ${{ secrets.GH_BOT_EMAIL }} - author_name: ${{ secrets.GH_BOT_NAME }} - branch: dist - directory: dist - github_token: ${{ secrets.GH_BOT_TOKEN }} - message: 'Update release assets - ${{ github.run_id }}' + github-token: ${{ secrets.GH_BOT_TOKEN }} + script: | + // Import the packages generation module + const { generatePackagesJson, writePackagesJson } = require('./.github/scripts/generate-packages.js'); - deploy-website: - needs: sync-assets - runs-on: ubuntu-latest - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - steps: - - name: Checkout repository - uses: actions/checkout@v5 - with: - token: ${{ secrets.GH_BOT_TOKEN }} + console.log('Generating packages.json from dist directory...'); - - name: Create or checkout gh-pages branch into gh-pages directory - run: | - git fetch origin - if git rev-parse --verify origin/gh-pages >/dev/null 2>&1; then - echo "gh-pages branch exists, checking it out" - git worktree add gh-pages origin/gh-pages - cd gh-pages - git pull origin gh-pages - else - echo "gh-pages branch doesn't exist, creating new one" - git worktree add --orphan gh-pages - cd gh-pages - git rm -rf . 2>/dev/null || true - fi + // Change to dist directory + process.chdir('./dist'); - - name: Copy website files to gh-pages directory - run: | - cp -r gh-pages-template/* gh-pages/ + // Generate the packages data + const packagesData = generatePackagesJson('.'); + + // Write the packages.json file + writePackagesJson(packagesData, './packages.json'); - name: Commit and push changes + if: github.event_name != 'pull_request' uses: actions-js/push@v1.5 with: author_email: ${{ secrets.GH_BOT_EMAIL }} author_name: ${{ secrets.GH_BOT_NAME }} - branch: gh-pages - directory: gh-pages + branch: dist + directory: dist github_token: ${{ secrets.GH_BOT_TOKEN }} - message: 'Update from ${{ github.sha }}' + message: 'Update release assets - ${{ github.run_id }}' diff --git a/.github/workflows/update-pages.yml b/.github/workflows/update-pages.yml new file mode 100644 index 00000000..030033ca --- /dev/null +++ b/.github/workflows/update-pages.yml @@ -0,0 +1,49 @@ +--- +name: Build GH-Pages +permissions: + contents: read + +on: + pull_request: + branches: + - master + types: + - opened + - synchronize + - reopened + push: + branches: + - master + workflow_dispatch: + +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + prep: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: prep + path: gh-pages-template/ + if-no-files-found: error + include-hidden-files: true + retention-days: 1 + + call-jekyll-build: + needs: prep + uses: LizardByte/LizardByte.github.io/.github/workflows/jekyll-build.yml@master + secrets: + GH_BOT_EMAIL: ${{ secrets.GH_BOT_EMAIL }} + GH_BOT_NAME: ${{ secrets.GH_BOT_NAME }} + GH_BOT_TOKEN: ${{ secrets.GH_BOT_TOKEN }} + with: + clean_gh_pages: true + site_artifact: 'prep' + target_branch: 'gh-pages' diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..681f205b --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,25 @@ +--- +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +build: + os: ubuntu-24.04 + tools: + ruby: "3.3" + apt_packages: + - 7zip + - jq + jobs: + install: + - | + mkdir -p "./tmp" + branch="master" + base_url="https://raw.githubusercontent.com/LizardByte/LizardByte.github.io" + url="${base_url}/refs/heads/${branch}/scripts/readthedocs_build.sh" + curl -sSL -o "./tmp/readthedocs_build.sh" "${url}" + chmod +x "./tmp/readthedocs_build.sh" + build: + html: + - "./tmp/readthedocs_build.sh" diff --git a/gh-pages-template/app.js b/gh-pages-template/app.js deleted file mode 100644 index f23e36dc..00000000 --- a/gh-pages-template/app.js +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Repository Data Manager - * Handles loading and managing repository data from GitHub API - */ -class RepositoryDataManager { - constructor() { - this.repositoryData = []; - this.orgName = 'LizardByte'; // Organization name - this.distBranch = 'dist'; - this.apiBase = 'https://api.github.com'; - this.rawBase = 'https://raw.githubusercontent.com'; - } - - /** - * Load repository data by scanning the dist branch via GitHub API - */ - async loadRepositoryData() { - try { - console.log('Loading repository data from GitHub API...'); - - // Get the contents of the dist branch - const response = await fetch(`${this.apiBase}/repos/${this.orgName}/packages/contents?ref=${this.distBranch}`); - - if (!response.ok) { - throw new Error(`GitHub API error: ${response.status}`); - } - - const contents = await response.json(); - - // Filter for directories (repositories) - const repoDirs = contents.filter(item => item.type === 'dir'); - - console.log(`Found ${repoDirs.length} repository directories`); - - this.repositoryData = []; - - // Process each repository directory - for (const repoDir of repoDirs) { - const repoData = await this.processRepository(repoDir.name); - if (repoData && repoData.releases.length > 0) { - this.repositoryData.push(repoData); - } - } - - console.log(`Loaded data for ${this.repositoryData.length} repositories with releases`); - - return { - repositories: this.repositoryData, - lastUpdated: new Date().toISOString(), - totalRepositories: this.repositoryData.length, - totalReleases: this.repositoryData.reduce((sum, repo) => sum + repo.releases.length, 0), - totalAssets: this.repositoryData.reduce((sum, repo) => - sum + repo.releases.reduce((releaseSum, release) => releaseSum + release.assetCount, 0), 0) - }; - - } catch (error) { - console.error('Error loading repository data:', error); - // Fallback to empty data - this.repositoryData = []; - return null; - } - } - - /** - * Process a single repository directory to get release information - */ - async processRepository(repoName) { - try { - console.log(`Processing repository: ${repoName}`); - - // Get repository directory contents - const response = await fetch(`${this.apiBase}/repos/${this.orgName}/packages/contents/${repoName}?ref=${this.distBranch}`); - - if (!response.ok) { - console.warn(`Could not fetch contents for ${repoName}: ${response.status}`); - return null; - } - - const contents = await response.json(); - - // Filter for directories (releases) - const releaseDirs = contents.filter(item => item.type === 'dir'); - - if (releaseDirs.length === 0) { - console.log(`No release directories found for ${repoName}`); - return null; - } - - const repoData = { - name: repoName, - releases: [] - }; - - // Process each release directory - for (const releaseDir of releaseDirs) { - const releaseData = await this.processRelease(repoName, releaseDir.name); - if (releaseData) { - repoData.releases.push(releaseData); - } - } - - // Sort releases by tag name (newest first, assuming semantic versioning) - repoData.releases.sort((a, b) => b.tag.localeCompare(a.tag, undefined, { numeric: true, sensitivity: 'base' })); - - return repoData; - - } catch (error) { - console.error(`Error processing repository ${repoName}:`, error); - return null; - } - } - - /** - * Process a single release directory to count assets - */ - async processRelease(repoName, releaseTag) { - try { - // Get release directory contents - const response = await fetch(`${this.apiBase}/repos/${this.orgName}/packages/contents/${repoName}/${releaseTag}?ref=${this.distBranch}`); - - if (!response.ok) { - console.warn(`Could not fetch release contents for ${repoName}/${releaseTag}: ${response.status}`); - return null; - } - - const contents = await response.json(); - - // Count actual asset files (exclude hash files) - const assetFiles = contents.filter(item => - item.type === 'file' && - !item.name.endsWith('.sha256') && - !item.name.endsWith('.sha512') && - !item.name.endsWith('.md5') && - item.name !== 'README.md' - ); - - if (assetFiles.length === 0) { - return null; - } - - return { - tag: releaseTag, - assetCount: assetFiles.length - }; - - } catch (error) { - console.error(`Error processing release ${repoName}/${releaseTag}:`, error); - return null; - } - } - - /** - * Get all repository data - */ - getRepositories() { - return this.repositoryData; - } - - /** - * Filter repositories based on search term - */ - filterRepositories(searchTerm) { - if (!searchTerm) { - return this.repositoryData; - } - - return this.repositoryData.filter(repo => { - const repoMatch = repo.name.toLowerCase().includes(searchTerm.toLowerCase()); - const releaseMatch = repo.releases.some(release => - release.tag.toLowerCase().includes(searchTerm.toLowerCase())); - return repoMatch || releaseMatch; - }); - } -} - -/** - * UI Manager - * Handles all DOM manipulation and rendering - */ -class UIManager { - constructor() { - this.repositoryGrid = document.getElementById('repositoryGrid'); - this.searchInput = document.getElementById('searchInput'); - this.repoCountElement = document.getElementById('repoCount'); - this.releaseCountElement = document.getElementById('releaseCount'); - this.assetCountElement = document.getElementById('assetCount'); - this.updateTimeElement = document.getElementById('updateTime'); - this.orgName = 'LizardByte'; - } - - /** - * Render repositories in the grid - */ - renderRepositories(repos) { - if (repos.length === 0) { - this.repositoryGrid.innerHTML = '
No repositories found.
'; - return; - } - - this.repositoryGrid.innerHTML = repos.map(repo => ` -
-

${repo.name}

- -
- `).join(''); - } - - /** - * Update statistics display - */ - updateStats(repos) { - const repoCount = repos.length; - const releaseCount = repos.reduce((sum, repo) => sum + repo.releases.length, 0); - const assetCount = repos.reduce((sum, repo) => - sum + repo.releases.reduce((releaseSum, release) => releaseSum + release.assetCount, 0), 0); - - this.repoCountElement.textContent = repoCount; - this.releaseCountElement.textContent = releaseCount; - this.assetCountElement.textContent = assetCount; - this.updateTimeElement.textContent = new Date().toLocaleString(); - } - - /** - * Show loading state - */ - showLoading() { - this.repositoryGrid.innerHTML = '
Loading repository data...
'; - this.repoCountElement.textContent = '-'; - this.releaseCountElement.textContent = '-'; - this.assetCountElement.textContent = '-'; - this.updateTimeElement.textContent = '-'; - } -} - -/** - * Search Manager - * Handles search functionality - */ -class SearchManager { - constructor(dataManager, uiManager) { - this.dataManager = dataManager; - this.uiManager = uiManager; - this.searchInput = document.getElementById('searchInput'); - this.initializeSearch(); - } - - /** - * Initialize search functionality - */ - initializeSearch() { - this.searchInput.addEventListener('input', (e) => { - this.performSearch(e.target.value); - }); - } - - /** - * Perform search and update UI - */ - performSearch(searchTerm) { - const filteredRepos = this.dataManager.filterRepositories(searchTerm); - this.uiManager.renderRepositories(filteredRepos); - } -} - -/** - * Main Application - * Coordinates all components and manages application state - */ -class LizardByteAssetsApp { - constructor() { - this.dataManager = new RepositoryDataManager(); - this.uiManager = new UIManager(); - this.searchManager = null; - } - - /** - * Initialize the application - */ - async init() { - try { - // Show loading state - this.uiManager.showLoading(); - - // Load repository data - const data = await this.dataManager.loadRepositoryData(); - const repositories = this.dataManager.getRepositories(); - - // Render repositories and update stats - this.uiManager.renderRepositories(repositories); - this.uiManager.updateStats(repositories); - - // Initialize search functionality - this.searchManager = new SearchManager(this.dataManager, this.uiManager); - - console.log(`Loaded ${repositories.length} repositories`); - - } catch (error) { - console.error('Failed to initialize application:', error); - this.uiManager.repositoryGrid.innerHTML = - '
Failed to load repository data. Please try again later.
'; - } - } -} - -// Initialize the application when the DOM is loaded -document.addEventListener('DOMContentLoaded', () => { - const app = new LizardByteAssetsApp(); - app.init(); -}); diff --git a/gh-pages-template/assets/css/packages.css b/gh-pages-template/assets/css/packages.css new file mode 100644 index 00000000..59793549 --- /dev/null +++ b/gh-pages-template/assets/css/packages.css @@ -0,0 +1,18 @@ +/* Custom styles for packages page - minimal overrides to Bootstrap 5.3 */ + +/* Custom hover effect for cards */ +.card:hover { + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; + transition: box-shadow 0.15s ease-in-out; +} + +/* Ensure links in list items don't break layout */ +.list-group-item a { + flex-grow: 1; +} + +/* Custom focus styles for search input to match theme */ +#searchInput:focus { + border-color: var(--bs-primary); + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); +} diff --git a/gh-pages-template/assets/js/app.js b/gh-pages-template/assets/js/app.js new file mode 100644 index 00000000..e96bddb0 --- /dev/null +++ b/gh-pages-template/assets/js/app.js @@ -0,0 +1,233 @@ +/** + * Repository Data Manager + * Handles loading and managing repository data from a single JSON file + */ +class RepositoryDataManager { + constructor() { + this.repositoryData = []; + this.orgName = 'LizardByte'; // Organization name + this.distBranch = 'dist'; + this.rawBase = 'https://raw.githubusercontent.com'; + } + + /** + * Load repository data from packages.json in the dist branch + */ + async loadRepositoryData() { + try { + console.log('Loading repository data from packages.json...'); + + // Fetch the packages.json file from the dist branch + const response = await fetch(`${this.rawBase}/${this.orgName}/packages/${this.distBranch}/packages.json`); + + if (!response.ok) { + throw new Error(`Failed to fetch packages.json: ${response.status}`); + } + + const data = await response.json(); + + // Validate the data structure + if (!data.repositories || !Array.isArray(data.repositories)) { + throw new Error('Invalid packages.json format: missing repositories array'); + } + + this.repositoryData = data.repositories; + + console.log(`Loaded data for ${this.repositoryData.length} repositories from packages.json`); + + return { + repositories: this.repositoryData, + lastUpdated: data.lastUpdated || new Date().toISOString(), + totalRepositories: this.repositoryData.length, + totalReleases: this.repositoryData.reduce((sum, repo) => sum + (repo.releases ? repo.releases.length : 0), 0), + totalAssets: this.repositoryData.reduce((sum, repo) => + sum + (repo.releases ? repo.releases.reduce((releaseSum, release) => releaseSum + (release.assetCount || 0), 0) : 0), 0) + }; + + } catch (error) { + console.error('Error loading repository data:', error); + // Fallback to empty data + this.repositoryData = []; + return { + repositories: [], + error: error.message, + lastUpdated: new Date().toISOString(), + totalRepositories: 0, + totalReleases: 0, + totalAssets: 0 + }; + } + } + + /** + * Get all repository data + */ + getRepositories() { + return this.repositoryData; + } + + /** + * Filter repositories based on search term + */ + filterRepositories(searchTerm) { + if (!searchTerm) { + return this.repositoryData; + } + + return this.repositoryData.filter(repo => { + const repoMatch = repo.name.toLowerCase().includes(searchTerm.toLowerCase()); + const releaseMatch = repo.releases && repo.releases.some(release => + release.tag.toLowerCase().includes(searchTerm.toLowerCase())); + return repoMatch || releaseMatch; + }); + } +} + +/** + * UI Manager + * Handles all DOM manipulation and rendering + */ +class UIManager { + constructor() { + this.repositoryGrid = document.getElementById('repositoryGrid'); + this.searchInput = document.getElementById('searchInput'); + this.repoCountElement = document.getElementById('repoCount'); + this.releaseCountElement = document.getElementById('releaseCount'); + this.assetCountElement = document.getElementById('assetCount'); + this.updateTimeElement = document.getElementById('updateTime'); + this.orgName = 'LizardByte'; + } + + /** + * Render repositories in the grid + */ + renderRepositories(repos) { + if (repos.length === 0) { + this.repositoryGrid.innerHTML = '
No repositories found.
'; + return; + } + + this.repositoryGrid.innerHTML = repos.map(repo => ` +
+
+
+
${repo.name}
+
    + ${repo.releases ? repo.releases.map(release => ` +
  • + + ${release.tag} + + ${release.assetCount} +
  • + `).join('') : '
  • No releases found
  • '} +
+
+
+
+ `).join(''); + } + + /** + * Update statistics display + */ + updateStats(repos) { + const repoCount = repos.length; + const releaseCount = repos.reduce((sum, repo) => sum + (repo.releases ? repo.releases.length : 0), 0); + const assetCount = repos.reduce((sum, repo) => + sum + (repo.releases ? repo.releases.reduce((releaseSum, release) => releaseSum + (release.assetCount || 0), 0) : 0), 0); + + this.repoCountElement.textContent = repoCount; + this.releaseCountElement.textContent = releaseCount; + this.assetCountElement.textContent = assetCount; + this.updateTimeElement.textContent = new Date().toLocaleString(); + } + + /** + * Show loading state + */ + showLoading() { + this.repositoryGrid.innerHTML = '
Loading repository data...
'; + this.repoCountElement.textContent = '-'; + this.releaseCountElement.textContent = '-'; + this.assetCountElement.textContent = '-'; + this.updateTimeElement.textContent = '-'; + } +} + +/** + * Search Manager + * Handles search functionality + */ +class SearchManager { + constructor(dataManager, uiManager) { + this.dataManager = dataManager; + this.uiManager = uiManager; + this.searchInput = document.getElementById('searchInput'); + this.initializeSearch(); + } + + /** + * Initialize search functionality + */ + initializeSearch() { + this.searchInput.addEventListener('input', (e) => { + this.performSearch(e.target.value); + }); + } + + /** + * Perform search and update UI + */ + performSearch(searchTerm) { + const filteredRepos = this.dataManager.filterRepositories(searchTerm); + this.uiManager.renderRepositories(filteredRepos); + } +} + +/** + * Main Application + * Coordinates all components and manages application state + */ +class LizardByteAssetsApp { + constructor() { + this.dataManager = new RepositoryDataManager(); + this.uiManager = new UIManager(); + this.searchManager = null; + } + + /** + * Initialize the application + */ + async init() { + try { + // Show loading state + this.uiManager.showLoading(); + + // Load repository data + const data = await this.dataManager.loadRepositoryData(); + const repositories = this.dataManager.getRepositories(); + + // Render repositories and update stats + this.uiManager.renderRepositories(repositories); + this.uiManager.updateStats(repositories); + + // Initialize search functionality + this.searchManager = new SearchManager(this.dataManager, this.uiManager); + + console.log(`Loaded ${repositories.length} repositories`); + + } catch (error) { + console.error('Failed to initialize application:', error); + this.uiManager.repositoryGrid.innerHTML = + '
Failed to load repository data. Please try again later.
'; + } + } +} + +// Initialize the application when the DOM is loaded +document.addEventListener('DOMContentLoaded', () => { + const app = new LizardByteAssetsApp(); + app.init(); +}); diff --git a/gh-pages-template/index.html b/gh-pages-template/index.html index f6759536..ee643e39 100644 --- a/gh-pages-template/index.html +++ b/gh-pages-template/index.html @@ -1,46 +1,61 @@ - - - - - - LizardByte Release Assets - - - -
-
-

LizardByte Release Assets

-

Centralized storage for all release assets from LizardByte repositories

-
+--- +title: Packages +subtitle: Centralized storage for release assets from LizardByte repositories +layout: page +full-width: true +after-content: +- donate.html +- support.html +css: +- /assets/css/packages.css +js: +- /assets/js/app.js +--- -
- +
+ +
+
+
+
-
-
-
-
-
Repositories
+
+
+
+
+
-
+

Repositories

+
-
-
-
-
Releases
+
+
+
+
+
-
+

Releases

+
-
-
-
-
Assets
+
+
+
+
+
-
+

Assets

+
+
-
-
Loading repository data...
-
+
+
Loading repository data...
+
-
- Last updated: - +
+
+
+ Last updated: - +
- - - - +
diff --git a/gh-pages-template/styles.css b/gh-pages-template/styles.css deleted file mode 100644 index 9f758ed1..00000000 --- a/gh-pages-template/styles.css +++ /dev/null @@ -1,180 +0,0 @@ -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif; - line-height: 1.6; - color: #24292f; - background-color: #ffffff; - padding: 20px; -} - -.container { - max-width: 1200px; - margin: 0 auto; -} - -header { - text-align: center; - margin-bottom: 40px; - padding-bottom: 20px; - border-bottom: 1px solid #d1d9e0; -} - -h1 { - color: #0969da; - margin-bottom: 10px; -} - -.subtitle { - color: #656d76; - font-size: 18px; -} - -.search-container { - margin-bottom: 30px; -} - -#searchInput { - width: 100%; - padding: 12px 16px; - border: 1px solid #d1d9e0; - border-radius: 6px; - font-size: 16px; - background-color: #f6f8fa; -} - -#searchInput:focus { - outline: none; - border-color: #0969da; - background-color: #ffffff; -} - -.stats { - display: flex; - justify-content: center; - gap: 30px; - margin-bottom: 30px; - flex-wrap: wrap; -} - -.stat { - text-align: center; - padding: 15px; - background-color: #f6f8fa; - border-radius: 6px; - min-width: 120px; -} - -.stat-number { - font-size: 24px; - font-weight: bold; - color: #0969da; -} - -.stat-label { - color: #656d76; - font-size: 14px; -} - -.repository-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - gap: 20px; -} - -.repository-card { - border: 1px solid #d1d9e0; - border-radius: 8px; - padding: 20px; - background-color: #ffffff; - transition: box-shadow 0.2s ease; -} - -.repository-card:hover { - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); -} - -.repository-name { - font-size: 18px; - font-weight: 600; - color: #0969da; - margin-bottom: 15px; - text-decoration: none; -} - -.repository-name:hover { - text-decoration: underline; -} - -.release-list { - list-style: none; -} - -.release-item { - padding: 8px 0; - border-bottom: 1px solid #f6f8fa; - display: flex; - justify-content: space-between; - align-items: center; -} - -.release-item:last-child { - border-bottom: none; -} - -.release-tag { - font-weight: 500; - color: #24292f; -} - -.asset-count { - font-size: 12px; - color: #656d76; - background-color: #f6f8fa; - padding: 2px 8px; - border-radius: 12px; -} - -.release-link { - text-decoration: none; - color: inherit; -} - -.release-link:hover .release-tag { - color: #0969da; -} - -.no-results { - text-align: center; - color: #656d76; - font-style: italic; - padding: 40px; -} - -.last-updated { - text-align: center; - margin-top: 40px; - padding-top: 20px; - border-top: 1px solid #d1d9e0; - color: #656d76; - font-size: 14px; -} - -@media (max-width: 768px) { - .repository-grid { - grid-template-columns: 1fr; - } - - .stats { - gap: 15px; - } - - .stat { - min-width: 100px; - padding: 10px; - } -}