diff --git a/.github/workflows/build-listing.yml b/.github/workflows/build-listing.yml new file mode 100644 index 0000000..ae9b415 --- /dev/null +++ b/.github/workflows/build-listing.yml @@ -0,0 +1,79 @@ +name: Build Repo Listing +# https://github.com/vrchat-community/template-package/blob/main/.github/workflows/build-listing.yml + +env: + listPublishDirectory: Website + pathToCi: ci + +on: + workflow_dispatch: + workflow_run: + workflows: [Build Release] + types: + - completed + release: + types: [published, created, edited, unpublished, deleted, released] + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + + # Build the VPM Listing Website and deploy to GitHub Pages + build-listing: + name: build-listing + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + + # Checkout Local Repository + - name: Checkout Local Repository + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac + + # Checkout Automation Repository without removing prior checkouts + - name: Checkout Automation Repository + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac + with: + repository: vrchat-community/package-list-action + path: ${{ env.pathToCi }} + clean: false + + # Load cached data from previous runs + - name: Restore Cache + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 + with: + path: | + ${{ env.pathToCi }}/.nuke/temp + ~/.nuget/packages + key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj') }} + + # Build Package Version Listing with Nuke + - name: Build Package Version Listing + run: ${{ env.pathToCi }}/build.cmd BuildRepoListing --root ${{ env.pathToCi }} --list-publish-directory $GITHUB_WORKSPACE/${{ env.listPublishDirectory }} --current-package-name ${{ vars.PACKAGE_NAME }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Prepare for GitHub Pages deployment + - name: Setup Pages + uses: actions/configure-pages@f156874f8191504dae5b037505266ed5dda6c382 + + # Upload the VPM Listing Website to GitHub Pages artifacts + - name: Upload Pages Artifact + uses: actions/upload-pages-artifact@a753861a5debcf57bf8b404356158c8e1e33150c + with: + path: ${{ env.listPublishDirectory }} + + # Deploy the uploaded VPM Listing Website to GitHub Pages + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@9dbe3824824f8a1377b8e298bafde1a50ede43e5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..aad8126 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,88 @@ +name: Build Release +# https://github.com/vrchat-community/template-package/blob/main/.github/workflows/release.yml + +on: + workflow_dispatch: + +jobs: + + # Validate Repository Configuration + config: + runs-on: ubuntu-latest + outputs: + config_package: ${{ steps.config_package.outputs.configPackage }} + steps: + + # Ensure that required repository variable has been created for the Package + - name: Validate Package Config + id: config_package + run: | + if [ "${{ vars.PACKAGE_NAME }}" != "" ]; then + echo "configPackage=true" >> $GITHUB_OUTPUT; + else + echo "configPackage=false" >> $GITHUB_OUTPUT; + fi + + # Build and release the Package + # If the repository is not configured properly, this job will be skipped + build: + needs: config + runs-on: ubuntu-latest + permissions: + contents: write + env: + packagePath: Packages/${{ vars.PACKAGE_NAME }} + if: needs.config.outputs.config_package == 'true' + steps: + + # Checkout Local Repository + - name: Checkout + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac + + # Get the Package version based on the package.json file + - name: Get Version + id: version + uses: zoexx/github-action-json-file-properties@b9f36ce6ee6fe2680cd3c32b2c62e22eade7e590 + with: + file_path: "${{ env.packagePath }}/package.json" + prop_path: "version" + + # Configure the Environment Variables needed for releasing the Package + - name: Set Environment Variables + run: | + echo "zipFile=${{ vars.PACKAGE_NAME }}-${{ steps.version.outputs.value }}".zip >> $GITHUB_ENV + echo "unityPackage=${{ vars.PACKAGE_NAME }}-${{ steps.version.outputs.value }}.unitypackage" >> $GITHUB_ENV + echo "version=${{ steps.version.outputs.value }}" >> $GITHUB_ENV + + # Zip the Package for release + - name: Create Package Zip + working-directory: "${{ env.packagePath }}" + run: zip -r "${{ github.workspace }}/${{ env.zipFile }}" . + + # Build a list of .meta files for future use + - name: Track Package Meta Files + run: find "${{ env.packagePath }}/" -name \*.meta >> metaList + + # Make a UnityPackage version of the Package for release + - name: Create UnityPackage + uses: pCYSl5EDgo/create-unitypackage@cfcd3cf0391a5ef1306342794866a9897c32af0b + with: + package-path: ${{ env.unityPackage }} + include-files: metaList + + # Make a release tag of the version from the package.json file + - name: Create Tag + id: tag_version + uses: rickstaa/action-create-tag@88dbf7ff6fe2405f8e8f6c6fdfd78829bc631f83 + with: + tag: "${{ env.version }}" + + # Publish the Release to GitHub + - name: Make Release + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 + with: + files: | + ${{ env.zipFile }} + ${{ env.unityPackage }} + ${{ env.packagePath }}/package.json + tag_name: ${{ env.version }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..284136d --- /dev/null +++ b/.gitignore @@ -0,0 +1,64 @@ +# This .gitignore file should be placed at the root of your Unity project directory +# +# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore +# +/[Ll]ibrary/ +/[Tt]emp/ +/[Oo]bj/ +/[Bb]uild/ +/[Bb]uilds/ +/[Ll]ogs/ +/[Mm]emoryCaptures/ + +# Asset meta data should only be ignored when the corresponding asset is also ignored +!/[Aa]ssets/**/*.meta + +# Uncomment this line if you wish to ignore the asset store tools plugin +# /[Aa]ssets/AssetStoreTools* + +# Autogenerated Jetbrains Rider plugin +[Aa]ssets/Plugins/Editor/JetBrains* + +# Visual Studio cache directory +.vs/ + +# Gradle cache directory +.gradle/ + +# Autogenerated VS/MD/Consulo solution and project files +ExportedObj/ +.consulo/ +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db + +# Unity3D generated meta files +*.pidb.meta +*.pdb.meta +*.mdb.meta + +# Unity3D generated file on crash reports +sysinfo.txt + +# Builds +*.apk +*.unitypackage + +# Crashlytics generated file +crashlytics-build.properties + +.idea/.idea.vpm-package-maker/.idea +Assets/PackageMakerWindowData.asset* +.idea +.vscode diff --git a/Packages/befuddledlabs.opensyncdance/Runtime.meta b/Packages/befuddledlabs.opensyncdance/Runtime.meta new file mode 100644 index 0000000..77bdc38 --- /dev/null +++ b/Packages/befuddledlabs.opensyncdance/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 810d1a3ce81415441b987db6bdc95618 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AacFlMachineLayerWrapper.cs b/Packages/befuddledlabs.opensyncdance/Runtime/AacFlMachineLayerWrapper.cs similarity index 98% rename from AacFlMachineLayerWrapper.cs rename to Packages/befuddledlabs.opensyncdance/Runtime/AacFlMachineLayerWrapper.cs index 0ef4c95..446c570 100644 --- a/AacFlMachineLayerWrapper.cs +++ b/Packages/befuddledlabs.opensyncdance/Runtime/AacFlMachineLayerWrapper.cs @@ -2,7 +2,7 @@ using AnimatorAsCode.V1; -namespace OpenSyncDance +namespace BefuddledLabs.OpenSyncDance { /// /// Wraps around the common interface of state machines and layers to make generating search trees easier. diff --git a/AacFlMachineLayerWrapper.cs.meta b/Packages/befuddledlabs.opensyncdance/Runtime/AacFlMachineLayerWrapper.cs.meta similarity index 100% rename from AacFlMachineLayerWrapper.cs.meta rename to Packages/befuddledlabs.opensyncdance/Runtime/AacFlMachineLayerWrapper.cs.meta diff --git a/OpenSyncDance.cs b/Packages/befuddledlabs.opensyncdance/Runtime/OpenSyncDance.cs similarity index 99% rename from OpenSyncDance.cs rename to Packages/befuddledlabs.opensyncdance/Runtime/OpenSyncDance.cs index fdf75aa..1c0f95f 100644 --- a/OpenSyncDance.cs +++ b/Packages/befuddledlabs.opensyncdance/Runtime/OpenSyncDance.cs @@ -10,7 +10,7 @@ using VRC.SDK3.Dynamics.Contact.Components; using VRC.SDKBase; -namespace OpenSyncDance +namespace BefuddledLabs.OpenSyncDance { [Serializable] diff --git a/OpenSyncDance.cs.meta b/Packages/befuddledlabs.opensyncdance/Runtime/OpenSyncDance.cs.meta similarity index 100% rename from OpenSyncDance.cs.meta rename to Packages/befuddledlabs.opensyncdance/Runtime/OpenSyncDance.cs.meta diff --git a/Utility.cs b/Packages/befuddledlabs.opensyncdance/Runtime/Utility.cs similarity index 98% rename from Utility.cs rename to Packages/befuddledlabs.opensyncdance/Runtime/Utility.cs index 4b378a5..759867f 100644 --- a/Utility.cs +++ b/Packages/befuddledlabs.opensyncdance/Runtime/Utility.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using AnimatorAsCode.V1; -namespace OpenSyncDance +namespace BefuddledLabs.OpenSyncDance { static class Utils { diff --git a/Utility.cs.meta b/Packages/befuddledlabs.opensyncdance/Runtime/Utility.cs.meta similarity index 100% rename from Utility.cs.meta rename to Packages/befuddledlabs.opensyncdance/Runtime/Utility.cs.meta diff --git a/Packages/befuddledlabs.opensyncdance/Runtime/opensyncdance.asmdef b/Packages/befuddledlabs.opensyncdance/Runtime/opensyncdance.asmdef new file mode 100644 index 0000000..86deb9f --- /dev/null +++ b/Packages/befuddledlabs.opensyncdance/Runtime/opensyncdance.asmdef @@ -0,0 +1,18 @@ +{ + "name": "befuddledlabs.opensyncdance", + "rootNamespace": "BefuddledLabs.OpenSyncDance", + "references": [ + "GUID:d689052aa981bf8459346a530f6e6678", + "GUID:71d9dcc7d30ab1c45866d01afa59b6cf", + "GUID:5718fb738711cd34ea54e9553040911d" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Packages/befuddledlabs.opensyncdance/Runtime/opensyncdance.asmdef.meta b/Packages/befuddledlabs.opensyncdance/Runtime/opensyncdance.asmdef.meta new file mode 100644 index 0000000..e07ae04 --- /dev/null +++ b/Packages/befuddledlabs.opensyncdance/Runtime/opensyncdance.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0716d6b1db7647d41837fb5428fb2531 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/befuddledlabs.opensyncdance/Samples.meta b/Packages/befuddledlabs.opensyncdance/Samples.meta new file mode 100644 index 0000000..004e6ef --- /dev/null +++ b/Packages/befuddledlabs.opensyncdance/Samples.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: acde519e5982f6c41b614b1890b44e44 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OSD_ExampleActionAnimator.controller b/Packages/befuddledlabs.opensyncdance/Samples/OSD_ExampleActionAnimator.controller similarity index 100% rename from OSD_ExampleActionAnimator.controller rename to Packages/befuddledlabs.opensyncdance/Samples/OSD_ExampleActionAnimator.controller diff --git a/OSD_ExampleActionAnimator.controller.meta b/Packages/befuddledlabs.opensyncdance/Samples/OSD_ExampleActionAnimator.controller.meta similarity index 100% rename from OSD_ExampleActionAnimator.controller.meta rename to Packages/befuddledlabs.opensyncdance/Samples/OSD_ExampleActionAnimator.controller.meta diff --git a/OSD_ExampleFXAnimator.controller b/Packages/befuddledlabs.opensyncdance/Samples/OSD_ExampleFXAnimator.controller similarity index 100% rename from OSD_ExampleFXAnimator.controller rename to Packages/befuddledlabs.opensyncdance/Samples/OSD_ExampleFXAnimator.controller diff --git a/OSD_ExampleFXAnimator.controller.meta b/Packages/befuddledlabs.opensyncdance/Samples/OSD_ExampleFXAnimator.controller.meta similarity index 100% rename from OSD_ExampleFXAnimator.controller.meta rename to Packages/befuddledlabs.opensyncdance/Samples/OSD_ExampleFXAnimator.controller.meta diff --git a/OpenSyncDanceTest.unity b/Packages/befuddledlabs.opensyncdance/Samples/OpenSyncDanceTest.unity similarity index 100% rename from OpenSyncDanceTest.unity rename to Packages/befuddledlabs.opensyncdance/Samples/OpenSyncDanceTest.unity diff --git a/OpenSyncDanceTest.unity.meta b/Packages/befuddledlabs.opensyncdance/Samples/OpenSyncDanceTest.unity.meta similarity index 100% rename from OpenSyncDanceTest.unity.meta rename to Packages/befuddledlabs.opensyncdance/Samples/OpenSyncDanceTest.unity.meta diff --git a/Packages/befuddledlabs.opensyncdance/package.json b/Packages/befuddledlabs.opensyncdance/package.json new file mode 100644 index 0000000..afd0478 --- /dev/null +++ b/Packages/befuddledlabs.opensyncdance/package.json @@ -0,0 +1,16 @@ +{ + "name": "befuddledlabs.opensyncdance", + "displayName": "Open Sync Dance", + "version": "0.0.1", + "url": "https://github.com/BefuddledLabs/OpenSyncDance", + "author": { + "name": "BefuddledLabs" + }, + "unity": "2022.3", + "description": "Tools for easier Avatar Creation", + "vpmDependencies": { + "com.vrchat.avatars": "^3.1.0", + "dev.hai-vr.animator-as-code.v1": "^1.0.9941", + "dev.hai-vr.animator-as-code.v1.vrchat": "^1.0.9941" + } +} \ No newline at end of file diff --git a/Packages/befuddledlabs.opensyncdance/package.json.meta b/Packages/befuddledlabs.opensyncdance/package.json.meta new file mode 100644 index 0000000..fc75daf --- /dev/null +++ b/Packages/befuddledlabs.opensyncdance/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f6c57710dd962994daf13d461d4dead0 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ca8b38 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# Open Sync Dance + +> [!WARNING] +> This package is under heavy development. Things may break! Use at your own risk. + +## Dependencies + +- [Haï's Animator As Code](https://github.com/hai-vr/av3-animator-as-code) +- [Haï's Animator As Code - VRChat](https://github.com/hai-vr/animator-as-code-vrchat) + +## Development + +To make modifications to this package: + +1. Clone this repository to a non-unity project folder. +2. In the **Unity** package manager: add package from disk. +3. The package should be editable via Unity and any external editor. + +## Acknowledgements + +- [*DeltaNeverUsed*](https://github.com/DeltaNeverUsed) 💻 +- [*Nara*](https://github.com/Naraenda) 💻 +- [*Airishayn*](https://x.com/Airishayn1/) 🎨 diff --git a/Website/app.js b/Website/app.js new file mode 100644 index 0000000..b15c8fe --- /dev/null +++ b/Website/app.js @@ -0,0 +1,231 @@ +import { baseLayerLuminance, StandardLuminance } from 'https://unpkg.com/@fluentui/web-components'; + +const LISTING_URL = "{{ listingInfo.Url }}"; + +const PACKAGES = { +{{~ for package in packages ~}} + "{{ package.Name }}": { + name: "{{ package.Name }}", + displayName: "{{ if package.DisplayName; package.DisplayName; end; }}", + description: "{{ if package.Description; package.Description; end; }}", + version: "{{ package.Version }}", + author: { + name: "{{ if package.Author.Name; package.Author.Name; end; }}", + url: "{{ if package.Author.Url; package.Author.Url; end; }}", + }, + dependencies: { + {{~ for dependency in package.Dependencies ~}} + "{{ dependency.Name }}": "{{ dependency.Version }}", + {{~ end ~}} + }, + keywords: [ + {{~ for keyword in package.Keywords ~}} + "{{ keyword }}", + {{~ end ~}} + ], + license: "{{ package.License }}", + licensesUrl: "{{ package.LicensesUrl }}", + }, +{{~ end ~}} +}; + +const setTheme = () => { + const isDarkTheme = () => window.matchMedia("(prefers-color-scheme: dark)").matches; + if (isDarkTheme()) { + baseLayerLuminance.setValueFor(document.documentElement, StandardLuminance.DarkMode); + } else { + baseLayerLuminance.setValueFor(document.documentElement, StandardLuminance.LightMode); + } +} + +(() => { + setTheme(); + + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + setTheme(); + }); + + const packageGrid = document.getElementById('packageGrid'); + + const searchInput = document.getElementById('searchInput'); + searchInput.addEventListener('input', ({ target: { value = '' }}) => { + const items = packageGrid.querySelectorAll('fluent-data-grid-row[row-type="default"]'); + items.forEach(item => { + if (value === '') { + item.style.display = 'grid'; + return; + } + if ( + item.dataset?.packageName?.toLowerCase()?.includes(value.toLowerCase()) || + item.dataset?.packageId?.toLowerCase()?.includes(value.toLowerCase()) + ) { + item.style.display = 'grid'; + } else { + item.style.display = 'none'; + } + }); + }); + + const urlBarHelpButton = document.getElementById('urlBarHelp'); + const addListingToVccHelp = document.getElementById('addListingToVccHelp'); + urlBarHelpButton.addEventListener('click', () => { + addListingToVccHelp.hidden = false; + }); + const addListingToVccHelpClose = document.getElementById('addListingToVccHelpClose'); + addListingToVccHelpClose.addEventListener('click', () => { + addListingToVccHelp.hidden = true; + }); + + const vccListingInfoUrlFieldCopy = document.getElementById('vccListingInfoUrlFieldCopy'); + vccListingInfoUrlFieldCopy.addEventListener('click', () => { + const vccUrlField = document.getElementById('vccListingInfoUrlField'); + vccUrlField.select(); + navigator.clipboard.writeText(vccUrlField.value); + vccUrlFieldCopy.appearance = 'accent'; + setTimeout(() => { + vccUrlFieldCopy.appearance = 'neutral'; + }, 1000); + }); + + const vccAddRepoButton = document.getElementById('vccAddRepoButton'); + vccAddRepoButton.addEventListener('click', () => window.location.assign(`vcc://vpm/addRepo?url=${encodeURIComponent(LISTING_URL)}`)); + + const vccUrlFieldCopy = document.getElementById('vccUrlFieldCopy'); + vccUrlFieldCopy.addEventListener('click', () => { + const vccUrlField = document.getElementById('vccUrlField'); + vccUrlField.select(); + navigator.clipboard.writeText(vccUrlField.value); + vccUrlFieldCopy.appearance = 'accent'; + setTimeout(() => { + vccUrlFieldCopy.appearance = 'neutral'; + }, 1000); + }); + + const rowMoreMenu = document.getElementById('rowMoreMenu'); + const hideRowMoreMenu = e => { + if (rowMoreMenu.contains(e.target)) return; + document.removeEventListener('click', hideRowMoreMenu); + rowMoreMenu.hidden = true; + } + + const rowMenuButtons = document.querySelectorAll('.rowMenuButton'); + rowMenuButtons.forEach(button => { + button.addEventListener('click', e => { + if (rowMoreMenu?.hidden) { + rowMoreMenu.style.top = `${e.clientY + e.target.clientHeight}px`; + rowMoreMenu.style.left = `${e.clientX - 120}px`; + rowMoreMenu.hidden = false; + + const downloadLink = rowMoreMenu.querySelector('#rowMoreMenuDownload'); + const downloadListener = () => { + window.open(e?.target?.dataset?.packageUrl, '_blank'); + } + downloadLink.addEventListener('change', () => { + downloadListener(); + downloadLink.removeEventListener('change', downloadListener); + }); + + setTimeout(() => { + document.addEventListener('click', hideRowMoreMenu); + }, 1); + } + }); + }); + + const packageInfoModal = document.getElementById('packageInfoModal'); + const packageInfoModalClose = document.getElementById('packageInfoModalClose'); + packageInfoModalClose.addEventListener('click', () => { + packageInfoModal.hidden = true; + }); + + // Fluent dialogs use nested shadow-rooted elements, so we need to use JS to style them + const modalControl = packageInfoModal.shadowRoot.querySelector('.control'); + modalControl.style.maxHeight = "90%"; + modalControl.style.transition = 'height 0.2s ease-in-out'; + modalControl.style.overflowY = 'hidden'; + + const packageInfoName = document.getElementById('packageInfoName'); + const packageInfoId = document.getElementById('packageInfoId'); + const packageInfoVersion = document.getElementById('packageInfoVersion'); + const packageInfoDescription = document.getElementById('packageInfoDescription'); + const packageInfoAuthor = document.getElementById('packageInfoAuthor'); + const packageInfoDependencies = document.getElementById('packageInfoDependencies'); + const packageInfoKeywords = document.getElementById('packageInfoKeywords'); + const packageInfoLicense = document.getElementById('packageInfoLicense'); + + const rowAddToVccButtons = document.querySelectorAll('.rowAddToVccButton'); + rowAddToVccButtons.forEach((button) => { + button.addEventListener('click', () => window.location.assign(`vcc://vpm/addRepo?url=${encodeURIComponent(LISTING_URL)}`)); + }); + + const rowPackageInfoButton = document.querySelectorAll('.rowPackageInfoButton'); + rowPackageInfoButton.forEach((button) => { + button.addEventListener('click', e => { + const packageId = e.target.dataset?.packageId; + const packageInfo = PACKAGES?.[packageId]; + if (!packageInfo) { + console.error(`Did not find package ${packageId}. Packages available:`, PACKAGES); + return; + } + + packageInfoName.textContent = packageInfo.displayName; + packageInfoId.textContent = packageId; + packageInfoVersion.textContent = `v${packageInfo.version}`; + packageInfoDescription.textContent = packageInfo.description; + packageInfoAuthor.textContent = packageInfo.author.name; + packageInfoAuthor.href = packageInfo.author.url; + + if ((packageInfo.keywords?.length ?? 0) === 0) { + packageInfoKeywords.parentElement.classList.add('hidden'); + } else { + packageInfoKeywords.parentElement.classList.remove('hidden'); + packageInfoKeywords.innerHTML = null; + packageInfo.keywords.forEach(keyword => { + const keywordDiv = document.createElement('div'); + keywordDiv.classList.add('me-2', 'mb-2', 'badge'); + keywordDiv.textContent = keyword; + packageInfoKeywords.appendChild(keywordDiv); + }); + } + + if (!packageInfo.license?.length && !packageInfo.licensesUrl?.length) { + packageInfoLicense.parentElement.classList.add('hidden'); + } else { + packageInfoLicense.parentElement.classList.remove('hidden'); + packageInfoLicense.textContent = packageInfo.license ?? 'See License'; + packageInfoLicense.href = packageInfo.licensesUrl ?? '#'; + } + + packageInfoDependencies.innerHTML = null; + Object.entries(packageInfo.dependencies).forEach(([name, version]) => { + const depRow = document.createElement('li'); + depRow.classList.add('mb-2'); + depRow.textContent = `${name} @ v${version}`; + packageInfoDependencies.appendChild(depRow); + }); + + packageInfoModal.hidden = false; + + setTimeout(() => { + const height = packageInfoModal.querySelector('.col').clientHeight; + modalControl.style.setProperty('--dialog-height', `${height + 14}px`); + }, 1); + }); + }); + + const packageInfoVccUrlFieldCopy = document.getElementById('packageInfoVccUrlFieldCopy'); + packageInfoVccUrlFieldCopy.addEventListener('click', () => { + const vccUrlField = document.getElementById('packageInfoVccUrlField'); + vccUrlField.select(); + navigator.clipboard.writeText(vccUrlField.value); + vccUrlFieldCopy.appearance = 'accent'; + setTimeout(() => { + vccUrlFieldCopy.appearance = 'neutral'; + }, 1000); + }); + + const packageInfoListingHelp = document.getElementById('packageInfoListingHelp'); + packageInfoListingHelp.addEventListener('click', () => { + addListingToVccHelp.hidden = false; + }); +})(); \ No newline at end of file diff --git a/Website/banner.png b/Website/banner.png new file mode 100644 index 0000000..11f5fff Binary files /dev/null and b/Website/banner.png differ diff --git a/Website/favicon.ico b/Website/favicon.ico new file mode 100644 index 0000000..a72240a Binary files /dev/null and b/Website/favicon.ico differ diff --git a/Website/index.html b/Website/index.html new file mode 100644 index 0000000..8bb119c --- /dev/null +++ b/Website/index.html @@ -0,0 +1,221 @@ + + + + + + VCC Listing + + + + + +
+
+ {{~ if listingInfo.BannerImage; ~}} +
+ {{~ end; ~}} +

+ {{~ listingInfo.Name ~}} +

+ {{~ if listingInfo.Description; ~}} +
{{ listingInfo.Description }}
+ {{~ end; ~}} +
+ {{~ if listingInfo.Author.Email; ~}} + + {{ listingInfo.Author.Email }} + + {{~ end; ~}} + + {{~ if listingInfo.InfoLink.Url ~}} + + {{~ end; ~}} +
+
+
+ + + Add to VCC + + + + + Copy + + + How to add a listing to your VCC + + + + + + +
+ +
+ +
+ + + + + + + Name + + + Type + + + + + {{~ for package in packages ~}} + + +
+
{{ package.DisplayName }}
+
{{ package.Description }}
+
{{ package.Name }}
+
+
+ + {{ package.Type }} + + + Add to VCC + + + + + + + + + + + +
+ {{~ end ~}} +
+
+ {{~ if listingInfo.InfoLink.Url ~}} + + {{~ end; ~}} +
+ + + diff --git a/Website/styles.css b/Website/styles.css new file mode 100644 index 0000000..ce999b0 --- /dev/null +++ b/Website/styles.css @@ -0,0 +1,356 @@ +:root { + color-scheme: light dark; +} + +* { + box-sizing: border-box; +} + +body { + padding: 0; + margin: 0; + min-width: 100vw; + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + color: var(--neutral-foreground-rest); +} + +.hidden { + display: none !important; +} + +.row { + display: flex; + flex-direction: row; +} + +.col { + display: flex; + flex-direction: column; +} + +.content { + max-width: 1000px; + width: 100%; + margin: 0 auto; +} + +.align-items-center { + align-items: center; +} + +.justify-content-between { + justify-content: space-between; +} + +.justify-content-end { + justify-content: flex-end; +} + +h1 { + margin-bottom: 0.5rem; +} + +.caption1 { + font-size: 1rem; + color: var(--neutral-foreground-hover); +} + +.caption2 { + font-size: 0.8rem; + margin-top: 0.25rem; + color: var(--neutral-foreground-rest); +} + +.packages { + margin: 0.5rem 0 1rem 0; + max-width: 90%; + padding: 0.25rem; + display: flex; + flex: 1; +} + +#packageGrid { + overflow-y: auto; + width: 100%; + max-height: 40rem; +} + +.packages .packageName { + font-size: 1.1rem; + font-weight: 600; + margin: 0.25rem 0; +} + +.searchBlock { + margin-top: 1rem; + width: 100%; + max-width: 90%; +} + +.searchBlock .root { + width: 100%; +} + +#searchInput { + width: 100%; +} + +.vccUrlField { + min-width: 450px; + max-width: 90%; + flex-grow:1; +} + +#addListingToVccHelp { + z-index: 11; +} + +#packageInfoModal { + z-index: 10; +} + +#rowMoreMenu { + top: 0; + left: 0; + position: absolute; + z-index: 10; +} + +#rowMoreMenu a { + display: block; + text-decoration: none; + color: var(--neutral-foreground-rest); +} + +.bannerImage { + aspect-ratio: 5 / 1; + border-radius: 6px; + max-width: 90%; + width: 100%; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + margin-bottom: 0.25rem; +} + +.badge { + border-radius: 4px; + padding: 0.25rem 0.5rem; + background-color: var(--neutral-fill-hover); +} + +.m-0 { + margin: 0; +} + +.m-1 { + margin: 0.25rem; +} + +.m-2 { + margin: 0.5rem; +} + +.m-3 { + margin: 0.75rem; +} + +.m-4 { + margin: 1rem; +} + +.m-5 { + margin: 2rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mt-5 { + margin-top: 2rem; +} + +.mb-1 { + margin-bottom: 0.25rem; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mb-3 { + margin-bottom: 0.75rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mb-5 { + margin-bottom: 2rem; +} + +.ms-1 { + margin-left: 0.25rem; +} + +.ms-2 { + margin-left: 0.5rem; +} + +.ms-3 { + margin-left: 0.75rem; +} + +.ms-4 { + margin-left: 1rem; +} + +.ms-5 { + margin-left: 2rem; +} + +.me-1 { + margin-right: 0.25rem; +} + +.me-2 { + margin-right: 0.5rem; +} + +.me-3 { + margin-right: 0.75rem; +} + +.me-4 { + margin-right: 1rem; +} + +.me-5 { + margin-right: 2rem; +} + +.p-1 { + padding: 0.25rem; +} + +.p-2 { + padding: 0.5rem; +} + +.p-3 { + padding: 0.75rem; +} + +.p-4 { + padding: 1rem; +} + +.p-5 { + padding: 2rem; +} + +.pt-1 { + padding-top: 0.25rem; +} + +.pt-2 { + padding-top: 0.5rem; +} + +.pt-3 { + padding-top: 0.75rem; +} + +.pt-4 { + padding-top: 1rem; +} + +.pt-5 { + padding-top: 2rem; +} + +.pb-1 { + padding-bottom: 0.25rem; +} + +.pb-2 { + padding-bottom: 0.5rem; +} + +.pb-3 { + padding-bottom: 0.75rem; +} + +.pb-4 { + padding-bottom: 1rem; +} + +.pb-5 { + padding-bottom: 2rem; +} + +.ps-1 { + padding-left: 0.25rem; +} + +.ps-2 { + padding-left: 0.5rem; +} + +.ps-3 { + padding-left: 0.75rem; +} + +.ps-4 { + padding-left: 1rem; +} + +.ps-5 { + padding-left: 2rem; +} + +.pe-1 { + padding-right: 0.25rem; +} + +.pe-2 { + padding-right: 0.5rem; +} + +.pe-3 { + padding-right: 0.75rem; +} + +.pe-4 { + padding-right: 1rem; +} + +.pe-5 { + padding-right: 2rem; +} + +.w-100 { + width: 100%; +} + +.flex-1 { + flex: 1; +} \ No newline at end of file