Redo version page (#777)
* Redo version page * More work on editing page * Make saving work * Finish version editing * Version creation (base) * Add creation buttons * Add file uploader to versions page * Add version file parsing * Finish PR * Fix version page responsiveness and use more consistent card design * Whoops that wasn't supposed to be there * Fixes + allow whole page dragging * Re-add lost merge data * Remove debug line * Move back to list btm Co-authored-by: Prospector <prospectordev@gmail.com>
This commit is contained in:
parent
0de19a09ad
commit
6f58e9e7bb
1
assets/images/utils/file.svg
Normal file
1
assets/images/utils/file.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 320 B |
1
assets/images/utils/hash.svg
Normal file
1
assets/images/utils/hash.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="9" x2="20" y2="9"></line><line x1="4" y1="15" x2="20" y2="15"></line><line x1="10" y1="3" x2="8" y2="21"></line><line x1="16" y1="3" x2="14" y2="21"></line></svg>
|
||||
|
After Width: | Height: | Size: 303 B |
@ -381,25 +381,25 @@ tr.button-transparent {
|
||||
background-color: transparent;
|
||||
border-radius: var(--size-rounded-sm);
|
||||
|
||||
&:focus-visible:not(&:disabled) > *,
|
||||
&:hover:not(&:disabled) > * {
|
||||
cursor: pointer;
|
||||
filter: brightness(0.85);
|
||||
background-color: var(--color-raised-bg);
|
||||
}
|
||||
&:focus-visible:not(&:disabled) > *,
|
||||
&:hover:not(&:disabled) > * {
|
||||
cursor: pointer;
|
||||
filter: brightness(0.85);
|
||||
background-color: var(--color-raised-bg);
|
||||
}
|
||||
|
||||
&:active:not(&:disabled) > * {
|
||||
filter: brightness(0.8);
|
||||
background-color: var(--color-raised-bg);
|
||||
}
|
||||
&:active:not(&:disabled) > * {
|
||||
filter: brightness(0.8);
|
||||
background-color: var(--color-raised-bg);
|
||||
}
|
||||
|
||||
&:disabled > *,
|
||||
&[disabled] > * {
|
||||
cursor: not-allowed;
|
||||
filter: grayscale(50%);
|
||||
opacity: 0.5;
|
||||
box-shadow: none;
|
||||
}
|
||||
&:disabled > *,
|
||||
&[disabled] > * {
|
||||
cursor: not-allowed;
|
||||
filter: grayscale(50%);
|
||||
opacity: 0.5;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.button-within {
|
||||
@ -886,6 +886,7 @@ textarea.known-error {
|
||||
}
|
||||
|
||||
.known-errors {
|
||||
min-height: 0;
|
||||
color: var(--color-special-red);
|
||||
|
||||
ul {
|
||||
@ -999,6 +1000,7 @@ h1 {
|
||||
color: var(--color-special-red);
|
||||
}
|
||||
}
|
||||
|
||||
.label__description {
|
||||
display: block;
|
||||
margin-block-end: var(--spacing-card-sm);
|
||||
@ -1067,11 +1069,14 @@ h1 {
|
||||
width: 15rem;
|
||||
}
|
||||
|
||||
>,
|
||||
.extend-styling> {
|
||||
input + *,
|
||||
.input-group + * {
|
||||
margin-block-start: var(--spacing-card-md);
|
||||
> :where(input + *, .input-group + *) {
|
||||
margin-block-start: var(--spacing-card-md);
|
||||
}
|
||||
|
||||
.input-group {
|
||||
.multiselect, input {
|
||||
width: auto;
|
||||
flex-basis: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1146,6 +1151,13 @@ h1 {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.full-width-inputs {
|
||||
.multiselect, input, .iconified-input {
|
||||
width: 100%;
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
&:disabled {
|
||||
@ -1356,3 +1368,10 @@ button {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ html {
|
||||
--color-banner-side: hsl(357, 78%, 40%);
|
||||
|
||||
--color-block-quote: var(--color-tooltip-bg);
|
||||
--color-header-underline: var(--color-tooltip-text);
|
||||
--color-header-underline: var(--color-divider-dark);
|
||||
--color-hr: var(--color-text);
|
||||
|
||||
--color-table-border: #dfe2e5;
|
||||
@ -156,7 +156,7 @@ html {
|
||||
--color-banner-side: hsl(357, 78%, 40%);
|
||||
|
||||
--color-block-quote: var(--color-code-bg);
|
||||
--color-header-underline: var(--color-tooltip-text);
|
||||
--color-header-underline: var(--color-divider-dark);
|
||||
--color-hr: var(--color-text);
|
||||
|
||||
--color-table-border: #4f5864;
|
||||
|
||||
@ -38,9 +38,26 @@
|
||||
}
|
||||
|
||||
.normal-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 0.75rem;
|
||||
display: grid;
|
||||
padding: 0 0.75rem;
|
||||
|
||||
grid-template:
|
||||
'sidebar'
|
||||
'content'
|
||||
'info'
|
||||
/ 100%;
|
||||
|
||||
.normal-page__sidebar {
|
||||
grid-area: sidebar;
|
||||
}
|
||||
|
||||
.normal-page__info {
|
||||
grid-area: info;
|
||||
}
|
||||
|
||||
.normal-page__content {
|
||||
grid-area: content;
|
||||
}
|
||||
}
|
||||
|
||||
.site-header {
|
||||
@ -49,13 +66,22 @@
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.normal-page {
|
||||
flex-direction: row;
|
||||
margin: 0 auto;
|
||||
max-width: 80rem;
|
||||
column-gap: 0.75rem;
|
||||
|
||||
grid-template:
|
||||
'sidebar content' auto
|
||||
'info content' auto
|
||||
'dummy content' 1fr
|
||||
/ 20rem 1fr;
|
||||
|
||||
&.alt-layout {
|
||||
flex-direction: row-reverse;
|
||||
grid-template:
|
||||
'content sidebar' auto
|
||||
'content info' auto
|
||||
'content dummy' 1fr
|
||||
/ 1fr 20rem;
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,11 +91,12 @@
|
||||
}
|
||||
|
||||
.normal-page__content {
|
||||
width: 60rem;
|
||||
max-width: 60rem;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
margin: 0 auto;
|
||||
max-width: calc(60rem - .75rem);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 80rem) {
|
||||
.normal-page__content {
|
||||
width: calc(60rem - .75rem);
|
||||
}
|
||||
}
|
||||
|
||||
82
components/ui/DropArea.vue
Normal file
82
components/ui/DropArea.vue
Normal file
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div
|
||||
ref="drop_area"
|
||||
class="drop-area"
|
||||
@drop.stop.prevent="
|
||||
(event) => {
|
||||
$refs.drop_area.style.visibility = 'hidden'
|
||||
|
||||
if (event.dataTransfer && event.dataTransfer.files) {
|
||||
$emit('change', event.dataTransfer.files)
|
||||
}
|
||||
}
|
||||
"
|
||||
@dragenter.prevent="allowDrag"
|
||||
@dragover.prevent="allowDrag"
|
||||
@dragleave.prevent="$refs.drop_area.style.visibility = 'hidden'"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DropArea',
|
||||
props: {
|
||||
accept: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('dragenter', () => {
|
||||
this.$refs.drop_area.style.visibility = 'visible'
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
allowDrag(event) {
|
||||
const file = event.dataTransfer?.items[0]
|
||||
|
||||
if (
|
||||
file &&
|
||||
this.accept
|
||||
.split(',')
|
||||
.reduce(
|
||||
(acc, t) => acc || file.type.startsWith(t) || file.type === t,
|
||||
false
|
||||
)
|
||||
) {
|
||||
// Adds file dropping indicator
|
||||
event.dataTransfer.dropEffect = 'copy'
|
||||
event.preventDefault()
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.drop-area {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10;
|
||||
visibility: hidden;
|
||||
background-color: hsla(0, 0%, 0%, 0.5);
|
||||
transition: visibility 0.2s ease-in-out, background-color 0.1s ease-in-out;
|
||||
display: flex;
|
||||
|
||||
&::before {
|
||||
--indent: 4rem;
|
||||
|
||||
content: ' ';
|
||||
position: relative;
|
||||
top: var(--indent);
|
||||
left: var(--indent);
|
||||
width: calc(100% - (2 * var(--indent)));
|
||||
height: calc(100% - (2 * var(--indent)));
|
||||
border-radius: 1rem;
|
||||
border: 0.25rem dashed var(--color-button-bg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,31 +1,26 @@
|
||||
<template>
|
||||
<div class="columns">
|
||||
<label
|
||||
class="iconified-button"
|
||||
@drop.prevent="handleDrop"
|
||||
@dragover.prevent
|
||||
>
|
||||
<UploadIcon v-if="showIcon" />
|
||||
{{ prompt }}
|
||||
<input
|
||||
type="file"
|
||||
:multiple="multiple"
|
||||
:accept="accept"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<label
|
||||
:class="{ 'long-style': longStyle }"
|
||||
@drop.prevent="handleDrop"
|
||||
@dragover.prevent
|
||||
>
|
||||
<slot></slot>
|
||||
{{ prompt }}
|
||||
<input
|
||||
type="file"
|
||||
:multiple="multiple"
|
||||
:accept="accept"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fileIsValid } from '~/plugins/fileUtils'
|
||||
import UploadIcon from '~/assets/images/utils/upload.svg?inline'
|
||||
|
||||
export default {
|
||||
name: 'FileInput',
|
||||
components: {
|
||||
UploadIcon,
|
||||
},
|
||||
components: {},
|
||||
props: {
|
||||
prompt: {
|
||||
type: String,
|
||||
@ -54,6 +49,10 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
longStyle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -92,22 +91,22 @@ label {
|
||||
svg {
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.known-error label {
|
||||
border-color: var(--color-special-red) !important;
|
||||
background-color: var(--color-warning-bg) !important;
|
||||
|
||||
span {
|
||||
border-color: var(--color-special-red);
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-warning-text);
|
||||
&.long-style {
|
||||
display: flex;
|
||||
padding: 1.5rem 2rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
grid-gap: 0.5rem;
|
||||
background-color: var(--color-button-bg);
|
||||
border-radius: var(--size-rounded-sm);
|
||||
border: dashed 0.3rem var(--color-text);
|
||||
cursor: pointer;
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
<template>
|
||||
<div class="layout" :class="{ 'expanded-mobile-nav': isBrowseMenuOpen }">
|
||||
<div
|
||||
ref="main_page"
|
||||
class="layout"
|
||||
:class="{ 'expanded-mobile-nav': isBrowseMenuOpen }"
|
||||
>
|
||||
<header class="site-header" role="presentation">
|
||||
<section class="navbar card columns" role="navigation">
|
||||
<section class="skip column" role="presentation">
|
||||
@ -581,7 +585,7 @@ export default {
|
||||
margin-bottom: var(--spacing-card-md);
|
||||
max-width: 100vw;
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
@media screen and (min-width: 1280px) {
|
||||
border-radius: var(--size-rounded-sm);
|
||||
max-width: 1280px;
|
||||
margin-left: auto;
|
||||
@ -793,15 +797,6 @@ export default {
|
||||
&.nuxt-link-exact-active {
|
||||
color: var(--color-button-text-active);
|
||||
background-color: var(--color-button-bg);
|
||||
|
||||
.profile-link {
|
||||
.username {
|
||||
margin-block: 0.7rem;
|
||||
}
|
||||
.prompt {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1024,12 +1019,6 @@ export default {
|
||||
color: var(--color-brand-inverted);
|
||||
background-color: var(--color-brand);
|
||||
|
||||
.profile-link {
|
||||
.prompt {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.beta-badge {
|
||||
background-color: var(--color-brand-inverted);
|
||||
color: var(--color-text-dark);
|
||||
|
||||
179
package-lock.json
generated
179
package-lock.json
generated
@ -8,6 +8,7 @@
|
||||
"name": "knossos",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@nuxtjs/axios": "^5.13.1",
|
||||
"@nuxtjs/dayjs": "^1.2.0",
|
||||
"@nuxtjs/markdownit": "^2.0.0",
|
||||
@ -16,6 +17,8 @@
|
||||
"cookie-universal-nuxt": "^2.1.5",
|
||||
"core-js": "^3.9.1",
|
||||
"highlight.js": "^10.3.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jszip": "^3.10.1",
|
||||
"nuxt": "^2.15.3",
|
||||
"sass": "^1.32.12",
|
||||
"v-tooltip": "^2.0.3",
|
||||
@ -1709,6 +1712,19 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@gar/promisify": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
|
||||
@ -1734,6 +1750,11 @@
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@iarna/toml": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz",
|
||||
"integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz",
|
||||
@ -5320,6 +5341,18 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig/node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig/node_modules/resolve-from": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
|
||||
@ -6871,6 +6904,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
@ -8395,6 +8441,11 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
|
||||
@ -9083,17 +9134,21 @@
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml/node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
},
|
||||
"node_modules/jsesc": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
||||
@ -9146,6 +9201,17 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/kind-of": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
|
||||
@ -9205,6 +9271,14 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
@ -13904,6 +13978,18 @@
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/nth-check": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
|
||||
@ -17708,6 +17794,16 @@
|
||||
"requires": {
|
||||
"type-fest": "^0.20.2"
|
||||
}
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -17733,6 +17829,11 @@
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||
"dev": true
|
||||
},
|
||||
"@iarna/toml": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz",
|
||||
"integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="
|
||||
},
|
||||
"@jridgewell/gen-mapping": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz",
|
||||
@ -20609,6 +20710,15 @@
|
||||
"resolve-from": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
|
||||
@ -21489,6 +21599,16 @@
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
@ -22925,6 +23045,11 @@
|
||||
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
|
||||
"dev": true
|
||||
},
|
||||
"immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
|
||||
},
|
||||
"immutable": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
|
||||
@ -23412,12 +23537,18 @@
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsesc": {
|
||||
@ -23460,6 +23591,17 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"jszip": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||
"requires": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
|
||||
@ -23510,6 +23652,14 @@
|
||||
"type-check": "~0.4.0"
|
||||
}
|
||||
},
|
||||
"lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"requires": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
@ -27247,6 +27397,15 @@
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
|
||||
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"nth-check": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
"fix": "eslint --fix --ext .js,.vue --ignore-path .eslintignore ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@nuxtjs/axios": "^5.13.1",
|
||||
"@nuxtjs/dayjs": "^1.2.0",
|
||||
"@nuxtjs/markdownit": "^2.0.0",
|
||||
@ -21,6 +22,8 @@
|
||||
"cookie-universal-nuxt": "^2.1.5",
|
||||
"core-js": "^3.9.1",
|
||||
"highlight.js": "^10.3.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jszip": "^3.10.1",
|
||||
"nuxt": "^2.15.3",
|
||||
"sass": "^1.32.12",
|
||||
"v-tooltip": "^2.0.3",
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
</div>
|
||||
</Modal>
|
||||
<ModalReport
|
||||
v-if="$auth.user"
|
||||
ref="modal_project_report"
|
||||
:item-id="project.id"
|
||||
item-type="project"
|
||||
@ -286,253 +287,249 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="extra-info-desktop card">
|
||||
<template
|
||||
v-if="
|
||||
project.issues_url ||
|
||||
project.source_url ||
|
||||
project.wiki_url ||
|
||||
project.discord_url ||
|
||||
project.donation_urls.length > 0
|
||||
</article>
|
||||
<div class="card normal-page__info">
|
||||
<template
|
||||
v-if="
|
||||
project.issues_url ||
|
||||
project.source_url ||
|
||||
project.wiki_url ||
|
||||
project.discord_url ||
|
||||
project.donation_urls.length > 0
|
||||
"
|
||||
>
|
||||
<h3 class="card-header">External resources</h3>
|
||||
<div class="links">
|
||||
<a
|
||||
v-if="project.issues_url"
|
||||
:href="project.issues_url"
|
||||
class="title"
|
||||
:target="$external()"
|
||||
>
|
||||
<IssuesIcon aria-hidden="true" />
|
||||
<span>Issues</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="project.source_url"
|
||||
:href="project.source_url"
|
||||
class="title"
|
||||
:target="$external()"
|
||||
>
|
||||
<CodeIcon aria-hidden="true" />
|
||||
<span>Source</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="project.wiki_url"
|
||||
:href="project.wiki_url"
|
||||
class="title"
|
||||
:target="$external()"
|
||||
>
|
||||
<WikiIcon aria-hidden="true" />
|
||||
<span>Wiki</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="project.discord_url"
|
||||
:href="project.discord_url"
|
||||
:target="$external()"
|
||||
>
|
||||
<DiscordIcon class="shrink" aria-hidden="true" />
|
||||
<span>Discord</span>
|
||||
</a>
|
||||
<a
|
||||
v-for="(donation, index) in project.donation_urls"
|
||||
:key="index"
|
||||
:href="donation.url"
|
||||
:target="$external()"
|
||||
>
|
||||
<BuyMeACoffeeLogo
|
||||
v-if="donation.id === 'bmac'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<PatreonIcon
|
||||
v-else-if="donation.id === 'patreon'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<KoFiIcon
|
||||
v-else-if="donation.id === 'ko-fi'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<PayPalIcon
|
||||
v-else-if="donation.id === 'paypal'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<OpenCollectiveIcon
|
||||
v-else-if="donation.id === 'open-collective'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<HeartIcon v-else-if="donation.id === 'github'" />
|
||||
<UnknownIcon v-else />
|
||||
<span v-if="donation.id === 'bmac'">Buy Me a Coffee</span>
|
||||
<span v-else-if="donation.id === 'patreon'">Patreon</span>
|
||||
<span v-else-if="donation.id === 'paypal'">PayPal</span>
|
||||
<span v-else-if="donation.id === 'ko-fi'">Ko-fi</span>
|
||||
<span v-else-if="donation.id === 'github'">GitHub Sponsors</span>
|
||||
<span v-else>Donate</span>
|
||||
</a>
|
||||
</div>
|
||||
<hr class="card-divider" />
|
||||
</template>
|
||||
<template v-if="featuredVersions.length > 0">
|
||||
<div class="featured-header">
|
||||
<h3 class="card-header">Featured versions</h3>
|
||||
<nuxt-link
|
||||
v-if="project.versions.length > 0 || currentMember"
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/versions`"
|
||||
class="goto-link"
|
||||
>
|
||||
See all
|
||||
<ChevronRightIcon
|
||||
class="featured-header-chevron"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div
|
||||
v-for="version in featuredVersions"
|
||||
:key="version.id"
|
||||
class="featured-version button-transparent"
|
||||
@click="
|
||||
$router.push(
|
||||
`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`
|
||||
)
|
||||
"
|
||||
>
|
||||
<h3 class="card-header">External resources</h3>
|
||||
<div class="links">
|
||||
<a
|
||||
v-if="project.issues_url"
|
||||
:href="project.issues_url"
|
||||
class="title"
|
||||
:target="$external()"
|
||||
>
|
||||
<IssuesIcon aria-hidden="true" />
|
||||
<span>Issues</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="project.source_url"
|
||||
:href="project.source_url"
|
||||
class="title"
|
||||
:target="$external()"
|
||||
>
|
||||
<CodeIcon aria-hidden="true" />
|
||||
<span>Source</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="project.wiki_url"
|
||||
:href="project.wiki_url"
|
||||
class="title"
|
||||
:target="$external()"
|
||||
>
|
||||
<WikiIcon aria-hidden="true" />
|
||||
<span>Wiki</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="project.discord_url"
|
||||
:href="project.discord_url"
|
||||
:target="$external()"
|
||||
>
|
||||
<DiscordIcon class="shrink" aria-hidden="true" />
|
||||
<span>Discord</span>
|
||||
</a>
|
||||
<a
|
||||
v-for="(donation, index) in project.donation_urls"
|
||||
:key="index"
|
||||
:href="donation.url"
|
||||
:target="$external()"
|
||||
>
|
||||
<BuyMeACoffeeLogo
|
||||
v-if="donation.id === 'bmac'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<PatreonIcon
|
||||
v-else-if="donation.id === 'patreon'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<KoFiIcon
|
||||
v-else-if="donation.id === 'ko-fi'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<PayPalIcon
|
||||
v-else-if="donation.id === 'paypal'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<OpenCollectiveIcon
|
||||
v-else-if="donation.id === 'open-collective'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<HeartIcon v-else-if="donation.id === 'github'" />
|
||||
<UnknownIcon v-else />
|
||||
<span v-if="donation.id === 'bmac'">Buy Me a Coffee</span>
|
||||
<span v-else-if="donation.id === 'patreon'">Patreon</span>
|
||||
<span v-else-if="donation.id === 'paypal'">PayPal</span>
|
||||
<span v-else-if="donation.id === 'ko-fi'">Ko-fi</span>
|
||||
<span v-else-if="donation.id === 'github'"
|
||||
>GitHub Sponsors</span
|
||||
>
|
||||
<span v-else>Donate</span>
|
||||
</a>
|
||||
</div>
|
||||
<hr class="card-divider" />
|
||||
</template>
|
||||
<template v-if="featuredVersions.length > 0">
|
||||
<div class="featured-header">
|
||||
<h3 class="card-header">Featured versions</h3>
|
||||
<a
|
||||
v-tooltip="
|
||||
findPrimary(version).filename +
|
||||
' (' +
|
||||
$formatBytes(findPrimary(version).size) +
|
||||
')'
|
||||
"
|
||||
:href="findPrimary(version).url"
|
||||
class="download download-button square-button brand-button"
|
||||
:title="`Download ${version.name}`"
|
||||
@click.stop="(event) => event.stopPropagation()"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
</a>
|
||||
<div class="info">
|
||||
<nuxt-link
|
||||
v-if="project.versions.length > 0 || currentMember"
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/versions`"
|
||||
class="goto-link"
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`"
|
||||
class="top"
|
||||
>
|
||||
See all
|
||||
<ChevronRightIcon
|
||||
class="featured-header-chevron"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ version.name }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div
|
||||
v-for="version in featuredVersions"
|
||||
:key="version.id"
|
||||
class="featured-version button-transparent"
|
||||
@click="
|
||||
$router.push(
|
||||
`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`
|
||||
)
|
||||
"
|
||||
>
|
||||
<a
|
||||
v-tooltip="
|
||||
findPrimary(version).filename +
|
||||
' (' +
|
||||
$formatBytes(findPrimary(version).size) +
|
||||
')'
|
||||
"
|
||||
:href="findPrimary(version).url"
|
||||
class="download square-button brand-button"
|
||||
:title="`Download ${version.name}`"
|
||||
@click.stop="(event) => event.stopPropagation()"
|
||||
<div
|
||||
v-if="version.game_versions.length > 0"
|
||||
class="game-version item"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
</a>
|
||||
<div class="info">
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`"
|
||||
class="top"
|
||||
>
|
||||
{{ version.name }}
|
||||
</nuxt-link>
|
||||
<div
|
||||
v-if="version.game_versions.length > 0"
|
||||
class="game-version item"
|
||||
>
|
||||
{{
|
||||
version.loaders.map((x) => $formatCategory(x)).join(', ')
|
||||
}}
|
||||
{{ $formatVersion(version.game_versions) }}
|
||||
</div>
|
||||
<Badge
|
||||
v-if="version.version_type === 'release'"
|
||||
type="release"
|
||||
color="green"
|
||||
/>
|
||||
<Badge
|
||||
v-else-if="version.version_type === 'beta'"
|
||||
type="beta"
|
||||
color="orange"
|
||||
/>
|
||||
<Badge
|
||||
v-else-if="version.version_type === 'alpha'"
|
||||
type="alpha"
|
||||
color="red"
|
||||
/>
|
||||
{{ version.loaders.map((x) => $formatCategory(x)).join(', ') }}
|
||||
{{ $formatVersion(version.game_versions) }}
|
||||
</div>
|
||||
</div>
|
||||
<hr class="card-divider" />
|
||||
</template>
|
||||
<h3 class="card-header">Project members</h3>
|
||||
<div
|
||||
v-for="member in members"
|
||||
:key="member.user.id"
|
||||
class="team-member columns button-transparent"
|
||||
@click="$router.push('/user/' + member.user.username)"
|
||||
>
|
||||
<Avatar
|
||||
:src="member.avatar_url"
|
||||
:alt="member.username"
|
||||
size="sm"
|
||||
circle
|
||||
/>
|
||||
|
||||
<div class="member-info">
|
||||
<nuxt-link :to="'/user/' + member.user.username" class="name">
|
||||
<p>{{ member.name }}</p>
|
||||
</nuxt-link>
|
||||
<p class="role">{{ member.role }}</p>
|
||||
<Badge
|
||||
v-if="version.version_type === 'release'"
|
||||
type="release"
|
||||
color="green"
|
||||
/>
|
||||
<Badge
|
||||
v-else-if="version.version_type === 'beta'"
|
||||
type="beta"
|
||||
color="orange"
|
||||
/>
|
||||
<Badge
|
||||
v-else-if="version.version_type === 'alpha'"
|
||||
type="alpha"
|
||||
color="red"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="card-divider" />
|
||||
<h3 class="card-header">Technical information</h3>
|
||||
<div class="infos">
|
||||
<div class="info">
|
||||
<div class="key">License</div>
|
||||
<div class="value lowercase">
|
||||
<a
|
||||
v-if="project.license.url"
|
||||
class="text-link"
|
||||
:href="project.license.url"
|
||||
>
|
||||
{{ licenseIdDisplay }}
|
||||
</a>
|
||||
<a
|
||||
v-else-if="
|
||||
project.license.id === 'LicenseRef-All-Rights-Reserved' ||
|
||||
!project.license.id.includes('LicenseRef')
|
||||
"
|
||||
class="text-link"
|
||||
@click="getLicenseData()"
|
||||
>
|
||||
{{ licenseIdDisplay }}
|
||||
</a>
|
||||
<span v-else>{{ licenseIdDisplay }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<h3 class="card-header">Project members</h3>
|
||||
<div
|
||||
v-for="member in members"
|
||||
:key="member.user.id"
|
||||
class="team-member columns button-transparent"
|
||||
@click="$router.push('/user/' + member.user.username)"
|
||||
>
|
||||
<Avatar
|
||||
:src="member.avatar_url"
|
||||
:alt="member.username"
|
||||
size="sm"
|
||||
circle
|
||||
/>
|
||||
|
||||
<div class="member-info">
|
||||
<nuxt-link :to="'/user/' + member.user.username" class="name">
|
||||
<p>{{ member.name }}</p>
|
||||
</nuxt-link>
|
||||
<p class="role">{{ member.role }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="card-divider" />
|
||||
<h3 class="card-header">Technical information</h3>
|
||||
<div class="infos">
|
||||
<div class="info">
|
||||
<div class="key">License</div>
|
||||
<div class="value lowercase">
|
||||
<a
|
||||
v-if="project.license.url"
|
||||
class="text-link"
|
||||
:href="project.license.url"
|
||||
>
|
||||
{{ licenseIdDisplay }}
|
||||
</a>
|
||||
<a
|
||||
v-else-if="
|
||||
project.license.id === 'LicenseRef-All-Rights-Reserved' ||
|
||||
!project.license.id.includes('LicenseRef')
|
||||
"
|
||||
class="text-link"
|
||||
@click="getLicenseData()"
|
||||
>
|
||||
{{ licenseIdDisplay }}
|
||||
</a>
|
||||
<span v-else>{{ licenseIdDisplay }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
project.project_type !== 'resourcepack' &&
|
||||
project.project_type !== 'plugin'
|
||||
"
|
||||
class="info"
|
||||
>
|
||||
<div class="key">Client side</div>
|
||||
<div class="value">
|
||||
{{ project.client_side }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
project.project_type !== 'resourcepack' &&
|
||||
project.project_type !== 'plugin'
|
||||
"
|
||||
class="info"
|
||||
>
|
||||
<div class="key">Client side</div>
|
||||
<div class="value">
|
||||
{{ project.client_side }}
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
project.project_type !== 'resourcepack' &&
|
||||
project.project_type !== 'plugin'
|
||||
"
|
||||
class="info"
|
||||
>
|
||||
<div class="key">Server side</div>
|
||||
<div class="value">
|
||||
{{ project.server_side }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
project.project_type !== 'resourcepack' &&
|
||||
project.project_type !== 'plugin'
|
||||
"
|
||||
class="info"
|
||||
>
|
||||
<div class="key">Server side</div>
|
||||
<div class="value">
|
||||
{{ project.server_side }}
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="key">Project ID</div>
|
||||
<div class="value lowercase">
|
||||
<CopyCode :text="project.id" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="key">Project ID</div>
|
||||
<div class="value lowercase">
|
||||
<CopyCode :text="project.id" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<section class="normal-page__content">
|
||||
<div
|
||||
v-if="project.status === 'unlisted'"
|
||||
@ -637,252 +634,6 @@
|
||||
:all-members.sync="allMembers"
|
||||
:dependencies.sync="dependencies"
|
||||
/>
|
||||
<div class="extra-info-mobile card">
|
||||
<template
|
||||
v-if="
|
||||
project.issues_url ||
|
||||
project.source_url ||
|
||||
project.wiki_url ||
|
||||
project.discord_url ||
|
||||
project.donation_urls.length > 0
|
||||
"
|
||||
>
|
||||
<h3 class="card-header">External resources</h3>
|
||||
<div class="links">
|
||||
<a
|
||||
v-if="project.issues_url"
|
||||
:href="project.issues_url"
|
||||
class="title"
|
||||
:target="$external()"
|
||||
>
|
||||
<IssuesIcon aria-hidden="true" />
|
||||
<span>Issues</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="project.source_url"
|
||||
:href="project.source_url"
|
||||
class="title"
|
||||
:target="$external()"
|
||||
>
|
||||
<CodeIcon aria-hidden="true" />
|
||||
<span>Source</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="project.wiki_url"
|
||||
:href="project.wiki_url"
|
||||
class="title"
|
||||
:target="$external()"
|
||||
>
|
||||
<WikiIcon aria-hidden="true" />
|
||||
<span>Wiki</span>
|
||||
</a>
|
||||
<a
|
||||
v-if="project.discord_url"
|
||||
:href="project.discord_url"
|
||||
:target="$external()"
|
||||
>
|
||||
<DiscordIcon class="shrink" aria-hidden="true" />
|
||||
<span>Discord</span>
|
||||
</a>
|
||||
<a
|
||||
v-for="(donation, index) in project.donation_urls"
|
||||
:key="index"
|
||||
:href="donation.url"
|
||||
:target="$external()"
|
||||
>
|
||||
<BuyMeACoffeeLogo
|
||||
v-if="donation.id === 'bmac'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<PatreonIcon
|
||||
v-else-if="donation.id === 'patreon'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<KoFiIcon
|
||||
v-else-if="donation.id === 'ko-fi'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<PayPalIcon
|
||||
v-else-if="donation.id === 'paypal'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<OpenCollectiveIcon
|
||||
v-else-if="donation.id === 'open-collective'"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<HeartIcon v-else-if="donation.id === 'github'" />
|
||||
<UnknownIcon v-else />
|
||||
<span v-if="donation.id === 'bmac'">Buy Me a Coffee</span>
|
||||
<span v-else-if="donation.id === 'patreon'">Patreon</span>
|
||||
<span v-else-if="donation.id === 'paypal'">PayPal</span>
|
||||
<span v-else-if="donation.id === 'ko-fi'">Ko-fi</span>
|
||||
<span v-else-if="donation.id === 'github'"
|
||||
>GitHub Sponsors</span
|
||||
>
|
||||
<span v-else>Donate</span>
|
||||
</a>
|
||||
</div>
|
||||
<hr class="card-divider" />
|
||||
</template>
|
||||
<template v-if="featuredVersions.length > 0">
|
||||
<div class="featured-header">
|
||||
<h3 class="card-header">Featured versions</h3>
|
||||
<nuxt-link
|
||||
v-if="project.versions.length > 0 || currentMember"
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/versions`"
|
||||
class="goto-link"
|
||||
>
|
||||
See all
|
||||
<ChevronRightIcon
|
||||
class="featured-header-chevron"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div
|
||||
v-for="version in featuredVersions"
|
||||
:key="version.id"
|
||||
class="featured-version button-transparent"
|
||||
@click="
|
||||
$router.push(
|
||||
`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`
|
||||
)
|
||||
"
|
||||
>
|
||||
<a
|
||||
v-tooltip="
|
||||
findPrimary(version).filename +
|
||||
' (' +
|
||||
$formatBytes(findPrimary(version).size) +
|
||||
')'
|
||||
"
|
||||
:href="findPrimary(version).url"
|
||||
class="download square-button brand-button"
|
||||
:title="`Download ${version.name}`"
|
||||
@click.stop="(event) => event.stopPropagation()"
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
</a>
|
||||
<div class="info">
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`"
|
||||
class="top"
|
||||
>
|
||||
{{ version.name }}
|
||||
</nuxt-link>
|
||||
<div
|
||||
v-if="version.game_versions.length > 0"
|
||||
class="game-version item"
|
||||
>
|
||||
{{
|
||||
version.loaders.map((x) => $formatCategory(x)).join(', ')
|
||||
}}
|
||||
{{ $formatVersion(version.game_versions) }}
|
||||
</div>
|
||||
<Badge
|
||||
v-if="version.version_type === 'release'"
|
||||
type="release"
|
||||
color="green"
|
||||
/>
|
||||
<Badge
|
||||
v-else-if="version.version_type === 'beta'"
|
||||
type="beta"
|
||||
color="orange"
|
||||
/>
|
||||
<Badge
|
||||
v-else-if="version.version_type === 'alpha'"
|
||||
type="alpha"
|
||||
color="red"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="card-divider" />
|
||||
</template>
|
||||
<h3 class="card-header">Project members</h3>
|
||||
<div
|
||||
v-for="member in members"
|
||||
:key="member.user.id"
|
||||
class="team-member columns button-transparent"
|
||||
@click="$router.push('/user/' + member.user.username)"
|
||||
>
|
||||
<Avatar
|
||||
:src="member.avatar_url"
|
||||
:alt="member.username"
|
||||
size="sm"
|
||||
circle
|
||||
/>
|
||||
|
||||
<div class="member-info">
|
||||
<nuxt-link :to="'/user/' + member.user.username" class="name">
|
||||
<p>{{ member.name }}</p>
|
||||
</nuxt-link>
|
||||
<p class="role">{{ member.role }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="card-divider" />
|
||||
<h3 class="card-header">Technical information</h3>
|
||||
<div class="infos">
|
||||
<div class="info">
|
||||
<div class="key">License</div>
|
||||
<div class="value lowercase">
|
||||
<a
|
||||
v-if="project.license.url"
|
||||
class="text-link"
|
||||
:href="project.license.url"
|
||||
>
|
||||
{{ licenseIdDisplay }}
|
||||
</a>
|
||||
<a
|
||||
v-else-if="
|
||||
project.license.id === 'LicenseRef-All-Rights-Reserved' ||
|
||||
!project.license.id.includes('LicenseRef')
|
||||
"
|
||||
class="text-link"
|
||||
@click="getLicenseData()"
|
||||
>
|
||||
{{ licenseIdDisplay }}
|
||||
</a>
|
||||
<span v-else>{{ licenseIdDisplay }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
project.project_type !== 'resourcepack' &&
|
||||
project.project_type !== 'plugin'
|
||||
"
|
||||
class="info"
|
||||
>
|
||||
<div class="key">Client side</div>
|
||||
<div class="value">
|
||||
{{ project.client_side }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
project.project_type !== 'resourcepack' &&
|
||||
project.project_type !== 'plugin'
|
||||
"
|
||||
class="info"
|
||||
>
|
||||
<div class="key">Server side</div>
|
||||
<div class="value">
|
||||
{{ project.server_side }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class="key">Project ID</div>
|
||||
<div class="value lowercase">
|
||||
<CopyCode :text="project.id" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@ -1205,7 +956,7 @@ export default {
|
||||
async submitForReview() {
|
||||
if (
|
||||
this.project.body === '' ||
|
||||
this.project.versions.length < 1 ||
|
||||
this.versions.length < 1 ||
|
||||
this.project.client_side === 'unknown' ||
|
||||
this.project.server_side === 'unknown'
|
||||
) {
|
||||
@ -1475,16 +1226,6 @@ export default {
|
||||
.content {
|
||||
max-width: calc(1280px - 21rem);
|
||||
}
|
||||
|
||||
.extra-info-mobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.extra-info-desktop {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.status-buttons {
|
||||
|
||||
@ -203,11 +203,13 @@
|
||||
:max-size="262144"
|
||||
:show-icon="true"
|
||||
accept="image/png,image/jpeg,image/gif,image/webp"
|
||||
class="choose-image"
|
||||
class="choose-image iconified-button"
|
||||
prompt="Choose image"
|
||||
:disabled="(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS"
|
||||
@change="showPreviewImage"
|
||||
/>
|
||||
>
|
||||
<UploadIcon />
|
||||
</FileInput>
|
||||
<button
|
||||
class="iconified-button"
|
||||
:disabled="(currentMember.permissions & EDIT_DETAILS) !== EDIT_DETAILS"
|
||||
@ -551,6 +553,7 @@ import PlusIcon from '~/assets/images/utils/plus.svg?inline'
|
||||
import SaveIcon from '~/assets/images/utils/save.svg?inline'
|
||||
import TrashIcon from '~/assets/images/utils/trash.svg?inline'
|
||||
import RevertIcon from '~/assets/images/utils/undo.svg?inline'
|
||||
import UploadIcon from '~/assets/images/utils/upload.svg?inline'
|
||||
|
||||
import Chips from '~/components/ui/Chips'
|
||||
import FileInput from '~/components/ui/FileInput'
|
||||
@ -570,6 +573,7 @@ export default {
|
||||
SaveIcon,
|
||||
TrashIcon,
|
||||
RevertIcon,
|
||||
UploadIcon,
|
||||
},
|
||||
props: {
|
||||
project: {
|
||||
|
||||
@ -237,8 +237,11 @@
|
||||
:max-size="5242880"
|
||||
accept="image/png,image/jpeg,image/gif,image/webp,.png,.jpeg,.gif,.webp"
|
||||
prompt="Choose image or drag it here"
|
||||
class="iconified-button"
|
||||
@change="(files) => showPreviewImage(files, index)"
|
||||
/>
|
||||
>
|
||||
<UploadIcon />
|
||||
</FileInput>
|
||||
<div class="gallery-buttons">
|
||||
<div class="delete-button-container">
|
||||
<button
|
||||
@ -268,6 +271,7 @@ import CheckIcon from '~/assets/images/utils/check.svg?inline'
|
||||
import ExternalIcon from '~/assets/images/utils/external.svg?inline'
|
||||
import ExpandIcon from '~/assets/images/utils/expand.svg?inline'
|
||||
import ContractIcon from '~/assets/images/utils/contract.svg?inline'
|
||||
import UploadIcon from '~/assets/images/utils/upload.svg?inline'
|
||||
|
||||
import FileInput from '~/components/ui/FileInput'
|
||||
import Checkbox from '~/components/ui/Checkbox'
|
||||
@ -287,6 +291,7 @@ export default {
|
||||
ExternalIcon,
|
||||
ExpandIcon,
|
||||
ContractIcon,
|
||||
UploadIcon,
|
||||
},
|
||||
auth: false,
|
||||
beforeRouteLeave(to, from, next) {
|
||||
|
||||
@ -20,10 +20,4 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.markdown-body {
|
||||
max-width: calc(
|
||||
60rem - 2 * var(--spacing-card-lg) - 9px
|
||||
); // $2.50 to anyone who can figure out why the 9px is needed
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,22 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<div v-if="currentMember" class="card header-buttons">
|
||||
<nuxt-link to="version/create" class="brand-button iconified-button">
|
||||
<PlusIcon />
|
||||
Create a version
|
||||
</nuxt-link>
|
||||
<FileInput
|
||||
:max-size="524288000"
|
||||
:accept="acceptFileFromProjectType(project.project_type)"
|
||||
prompt="Upload a version"
|
||||
class="brand-button iconified-button"
|
||||
@change="handleFiles"
|
||||
>
|
||||
<UploadIcon />
|
||||
</FileInput>
|
||||
<span class="indicator">
|
||||
<InfoIcon /> Click to choose a file or drag one onto this page
|
||||
</span>
|
||||
<DropArea
|
||||
:accept="acceptFileFromProjectType(project.project_type)"
|
||||
@change="handleFiles"
|
||||
/>
|
||||
</div>
|
||||
<VersionFilterControl
|
||||
class="card"
|
||||
@ -45,7 +57,14 @@
|
||||
>
|
||||
<DownloadIcon aria-hidden="true" />
|
||||
</a>
|
||||
<span class="version__title">{{ version.name }}</span>
|
||||
<nuxt-link
|
||||
:to="`/${project.project_type}/${
|
||||
project.slug ? project.slug : project.id
|
||||
}/version/${encodeURI(version.displayUrlEnding)}`"
|
||||
class="version__title"
|
||||
>
|
||||
{{ version.name }}
|
||||
</nuxt-link>
|
||||
<div class="version__metadata">
|
||||
<VersionBadge
|
||||
v-if="version.version_type === 'release'"
|
||||
@ -88,17 +107,24 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import PlusIcon from '~/assets/images/utils/plus.svg?inline'
|
||||
import { acceptFileFromProjectType } from '~/plugins/fileUtils'
|
||||
import DownloadIcon from '~/assets/images/utils/download.svg?inline'
|
||||
import UploadIcon from '~/assets/images/utils/upload.svg?inline'
|
||||
import InfoIcon from '~/assets/images/utils/info.svg?inline'
|
||||
import VersionBadge from '~/components/ui/Badge'
|
||||
import FileInput from '~/components/ui/FileInput'
|
||||
import VersionFilterControl from '~/components/ui/VersionFilterControl'
|
||||
import DropArea from '~/components/ui/DropArea.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PlusIcon,
|
||||
DropArea,
|
||||
DownloadIcon,
|
||||
UploadIcon,
|
||||
InfoIcon,
|
||||
VersionBadge,
|
||||
VersionFilterControl,
|
||||
FileInput,
|
||||
},
|
||||
auth: false,
|
||||
props: {
|
||||
@ -167,9 +193,20 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
acceptFileFromProjectType,
|
||||
updateVersions(updatedVersions) {
|
||||
this.filteredVersions = updatedVersions
|
||||
},
|
||||
handleFiles(files) {
|
||||
this.$router.push({
|
||||
name: 'type-id-version-create',
|
||||
params: {
|
||||
type: this.project.project_type,
|
||||
id: this.project.slug ? this.project.slug : this.project.id,
|
||||
newPrimaryFile: files[0],
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -177,7 +214,15 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
.header-buttons {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
.indicator {
|
||||
display: flex;
|
||||
gap: 0.5ch;
|
||||
align-items: center;
|
||||
color: var(--color-text-inactive);
|
||||
}
|
||||
}
|
||||
|
||||
.all-versions {
|
||||
@ -215,7 +260,10 @@ export default {
|
||||
|
||||
.version-button {
|
||||
display: grid;
|
||||
grid-template: 'download title supports stats' 'download metadata supports stats';
|
||||
grid-template:
|
||||
'download title supports stats'
|
||||
'download metadata supports stats'
|
||||
'download dummy supports stats';
|
||||
grid-template-columns: calc(2.25rem + var(--spacing-card-sm)) 1fr 1fr 1fr;
|
||||
column-gap: var(--spacing-card-sm);
|
||||
justify-content: left;
|
||||
@ -287,4 +335,14 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-create {
|
||||
padding: var(--spacing-card-bg);
|
||||
|
||||
.input-group {
|
||||
width: fit-content;
|
||||
margin-left: auto;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -23,10 +23,12 @@
|
||||
:max-size="262144"
|
||||
:show-icon="true"
|
||||
accept="image/png,image/jpeg,image/gif,image/webp"
|
||||
class="choose-image"
|
||||
class="choose-image iconified-button"
|
||||
prompt="Upload avatar"
|
||||
@change="showPreviewImage"
|
||||
/>
|
||||
>
|
||||
<UploadIcon />
|
||||
</FileInput>
|
||||
<button
|
||||
v-else-if="$auth.user && $auth.user.id === user.id"
|
||||
class="iconified-button"
|
||||
@ -269,6 +271,7 @@ import SaveIcon from '~/assets/images/utils/save.svg?inline'
|
||||
import GridIcon from '~/assets/images/utils/grid.svg?inline'
|
||||
import ListIcon from '~/assets/images/utils/list.svg?inline'
|
||||
import ImageIcon from '~/assets/images/utils/image.svg?inline'
|
||||
import UploadIcon from '~/assets/images/utils/upload.svg?inline'
|
||||
import FileInput from '~/components/ui/FileInput'
|
||||
import ModalReport from '~/components/ui/ModalReport'
|
||||
import ModalCreation from '~/components/ui/ModalCreation'
|
||||
@ -302,6 +305,7 @@ export default {
|
||||
GridIcon,
|
||||
ListIcon,
|
||||
ImageIcon,
|
||||
UploadIcon,
|
||||
},
|
||||
async asyncData(data) {
|
||||
try {
|
||||
@ -322,52 +326,13 @@ export default {
|
||||
}
|
||||
|
||||
let gitHubUser = {}
|
||||
let versions = []
|
||||
|
||||
try {
|
||||
const [gitHubUserData, versionsData] = (
|
||||
await Promise.all([
|
||||
data.$axios.get(`https://api.github.com/user/` + user.github_id),
|
||||
data.$axios.get(
|
||||
`versions?ids=${JSON.stringify(
|
||||
[].concat.apply(
|
||||
[],
|
||||
projects.map((x) => x.versions)
|
||||
)
|
||||
)}`
|
||||
),
|
||||
])
|
||||
).map((it) => it.data)
|
||||
|
||||
gitHubUser = gitHubUserData
|
||||
versions = versionsData
|
||||
gitHubUser = (
|
||||
await data.$axios.get(`https://api.github.com/user/` + user.github_id)
|
||||
).data
|
||||
} catch {}
|
||||
|
||||
for (const version of versions) {
|
||||
const projectIndex = projects.findIndex(
|
||||
(x) => x.id === version.project_id
|
||||
)
|
||||
|
||||
if (projects[projectIndex].loaders) {
|
||||
for (const loader of version.loaders) {
|
||||
if (!projects[projectIndex].loaders.includes(loader)) {
|
||||
projects[projectIndex].loaders.push(loader)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
projects[projectIndex].loaders = version.loaders
|
||||
}
|
||||
}
|
||||
|
||||
for (const project of projects) {
|
||||
project.categories = project.categories.concat(project.loaders)
|
||||
|
||||
project.project_type = data.$getProjectTypeForUrl(
|
||||
project.project_type,
|
||||
project.categories
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
projects,
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import JSZip from 'jszip'
|
||||
import TOML from '@iarna/toml'
|
||||
import yaml from 'js-yaml'
|
||||
import { formatBytes } from '~/plugins/shorthands'
|
||||
|
||||
/**
|
||||
@ -7,7 +10,7 @@ import { formatBytes } from '~/plugins/shorthands'
|
||||
* @param validationOptions.maxSize the max file size in bytes
|
||||
* @param validationOptions.alertOnInvalid if an alert should pop up describing
|
||||
* each validation error
|
||||
* @returns `true` if the file is valid; `false` otherise
|
||||
* @returns `true` if the file is valid; `false` otherwise
|
||||
*/
|
||||
export const fileIsValid = (file, validationOptions) => {
|
||||
const { maxSize, alertOnInvalid } = validationOptions
|
||||
@ -25,3 +28,212 @@ export const fileIsValid = (file, validationOptions) => {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export const acceptFileFromProjectType = (projectType) => {
|
||||
switch (projectType) {
|
||||
case 'mod':
|
||||
return '.jar,.zip,.litemod,application/java-archive,application/zip'
|
||||
case 'plugin':
|
||||
return '.jar,.zip,application/java-archive,application/zip'
|
||||
case 'resourcepack':
|
||||
return '.zip,application/zip'
|
||||
case 'shader':
|
||||
return '.zip,application/zip'
|
||||
case 'modpack':
|
||||
return '.mrpack,application/x-modrinth-modpack+zip'
|
||||
default:
|
||||
return '*'
|
||||
}
|
||||
}
|
||||
|
||||
export const inferVersionInfo = async function (
|
||||
rawFile,
|
||||
project,
|
||||
gameVersions
|
||||
) {
|
||||
function versionType(number) {
|
||||
if (number.includes('alpha')) {
|
||||
return 'alpha'
|
||||
} else if (
|
||||
number.includes('beta') ||
|
||||
number.match(/[^A-z](rc)[^A-z]/) || // includes `rc`
|
||||
number.match(/[^A-z](pre)[^A-z]/) // includes `pre`
|
||||
) {
|
||||
return 'beta'
|
||||
} else {
|
||||
return 'release'
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This func does not handle accurate semver parsing. We should eventually
|
||||
function gameVersionRange(gameVersionString, gameVersions) {
|
||||
if (!gameVersionString) {
|
||||
return []
|
||||
}
|
||||
|
||||
// Truncate characters after `-` & `+`
|
||||
const gameString = gameVersionString.replace(/-|\+.*$/g, '')
|
||||
|
||||
let prefix = ''
|
||||
if (gameString.includes('~')) {
|
||||
// Include minor versions
|
||||
// ~1.2.3 -> 1.2
|
||||
prefix = gameString.replace('~', '').split('.').slice(0, 2).join('.')
|
||||
} else if (gameString.includes('>=')) {
|
||||
// Include minor versions
|
||||
// >=1.2.3 -> 1.2
|
||||
prefix = gameString.replace('>=', '').split('.').slice(0, 2).join('.')
|
||||
} else if (gameString.includes('^')) {
|
||||
// Include major versions
|
||||
// ^1.2.3 -> 1
|
||||
prefix = gameString.replace('^', '').split('.')[0]
|
||||
} else if (gameString.includes('x')) {
|
||||
// Include versions that match `x.x.x`
|
||||
// 1.2.x -> 1.2
|
||||
prefix = gameString.replace(/\.x$/, '')
|
||||
} else {
|
||||
// Include exact version
|
||||
// 1.2.3 -> 1.2.3
|
||||
prefix = gameString
|
||||
}
|
||||
|
||||
const simplified = gameVersions
|
||||
.filter((it) => it.version_type === 'release')
|
||||
.map((it) => it.version)
|
||||
return simplified.filter((version) => version.startsWith(prefix))
|
||||
}
|
||||
|
||||
const inferFunctions = {
|
||||
// Forge 1.13+
|
||||
'META-INF/mods.toml': (file, zip) => {
|
||||
const metadata = TOML.parse(file)
|
||||
|
||||
console.log(JSON.stringify(metadata))
|
||||
|
||||
// TODO: Parse minecraft version ranges, handle if version is set to value from manifest
|
||||
if (metadata.mods && metadata.mods.length > 0) {
|
||||
return {
|
||||
name: `${project.title} ${metadata.mods[0].version}`,
|
||||
version_number: metadata.mods[0].version,
|
||||
version_type: versionType(metadata.mods[0].version),
|
||||
loaders: ['forge'],
|
||||
}
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
// Old Forge
|
||||
'mcmod.info': (file) => {
|
||||
const metadata = JSON.parse(file)
|
||||
|
||||
return {
|
||||
name: metadata.version ? `${project.title} ${metadata.version}` : '',
|
||||
version_number: metadata.version,
|
||||
version_type: versionType(metadata.version),
|
||||
loaders: ['forge'],
|
||||
game_versions: gameVersions
|
||||
.filter(
|
||||
(x) =>
|
||||
x.version.startsWith(metadata.mcversion) &&
|
||||
x.version_type === 'release'
|
||||
)
|
||||
.map((x) => x.version),
|
||||
}
|
||||
},
|
||||
// Fabric
|
||||
'fabric.mod.json': (file) => {
|
||||
const metadata = JSON.parse(file)
|
||||
|
||||
return {
|
||||
name: `${project.title} ${metadata.version}`,
|
||||
version_number: metadata.version,
|
||||
loaders: ['fabric'],
|
||||
version_type: versionType(metadata.version),
|
||||
game_versions: metadata.depends
|
||||
? gameVersionRange(metadata.depends.minecraft, gameVersions)
|
||||
: [],
|
||||
}
|
||||
},
|
||||
// Quilt
|
||||
'quilt.mod.json': (file) => {
|
||||
const metadata = JSON.parse(file)
|
||||
|
||||
return {
|
||||
name: `${project.title} ${metadata.quilt_loader.version}`,
|
||||
version_number: metadata.quilt_loader.version,
|
||||
loaders: ['quilt'],
|
||||
version_type: versionType(metadata.quilt_loader.version),
|
||||
game_versions: metadata.quilt_loader.depends
|
||||
? gameVersionRange(
|
||||
metadata.depends.find((x) => x.id === 'minecraft')
|
||||
? metadata.depends.find((x) => x.id === 'minecraft').versions
|
||||
: [],
|
||||
gameVersions
|
||||
)
|
||||
: [],
|
||||
}
|
||||
},
|
||||
// Bukkit + Other Forks
|
||||
'plugin.yml': (file) => {
|
||||
const metadata = yaml.load(file)
|
||||
|
||||
return {
|
||||
name: `${project.title} ${metadata.version}`,
|
||||
version_number: metadata.version,
|
||||
version_type: versionType(metadata.version),
|
||||
// We don't know which fork of Bukkit users are using
|
||||
loaders: [],
|
||||
game_versions: gameVersions
|
||||
.filter(
|
||||
(x) =>
|
||||
x.version.startsWith(metadata['api-version']) &&
|
||||
x.version_type === 'release'
|
||||
)
|
||||
.map((x) => x.version),
|
||||
}
|
||||
},
|
||||
// Bungeecord + Waterfall
|
||||
'bungee.yml': (file) => {
|
||||
const metadata = yaml.load(file)
|
||||
|
||||
return {
|
||||
name: `${project.title} ${metadata.version}`,
|
||||
version_number: metadata.version,
|
||||
version_type: versionType(metadata.version),
|
||||
loaders: ['bungeecord'],
|
||||
}
|
||||
},
|
||||
// Modpacks
|
||||
'modrinth.index.json': (file) => {
|
||||
const metadata = JSON.parse(file)
|
||||
|
||||
const loaders = []
|
||||
if ('forge' in metadata.dependencies) loaders.push('forge')
|
||||
if ('fabric-loader' in metadata.dependencies) loaders.push('fabric')
|
||||
if ('quilt-loader' in metadata.dependencies) loaders.push('quilt')
|
||||
|
||||
return {
|
||||
name: `${project.title} ${metadata.versionId}`,
|
||||
version_number: metadata.versionId,
|
||||
version_type: versionType(metadata.versionId),
|
||||
loaders,
|
||||
game_versions: gameVersions
|
||||
.filter((x) => x.version === metadata.dependencies.minecraft)
|
||||
.map((x) => x.version),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const zipReader = new JSZip()
|
||||
|
||||
const zip = await zipReader.loadAsync(rawFile)
|
||||
|
||||
for (const fileName in inferFunctions) {
|
||||
const file = zip.file(fileName)
|
||||
|
||||
if (file !== null) {
|
||||
const text = await file.async('text')
|
||||
return inferFunctions[fileName](text, zip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user