├── public ├── favicon.ico └── index.html ├── src ├── assets │ ├── logo.png │ ├── icon-fullscreen-close.svg │ ├── icon-fullscreen-open.svg │ └── icon-close.svg ├── main.js ├── components │ ├── ActiveFilters.vue │ ├── DarkmodeToggle.vue │ ├── FilterAside.vue │ └── GalleryComponent.vue ├── App.vue └── store │ └── index.js ├── babel.config.js ├── .gitignore ├── jsconfig.json ├── vue.config.js ├── config.js ├── package.json └── README.md /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nftchef/art-engine-viewer/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nftchef/art-engine-viewer/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import store from './store' 4 | 5 | createApp(App).use(store).mount('#app') 6 | -------------------------------------------------------------------------------- /src/assets/icon-fullscreen-close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icon-fullscreen-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icon-close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const config = require("./config"); 2 | console.log({ config }); 3 | process.env.VUE_APP_IMAGE_TYPE = config.imageType; 4 | 5 | const { defineConfig } = require("@vue/cli-service"); 6 | module.exports = defineConfig({ 7 | transpileDependencies: true, 8 | }); 9 | 10 | const path = require("path"); 11 | module.exports = { 12 | chainWebpack: (config) => { 13 | //TODO: make paths dynamic 14 | config.resolve.alias.set( 15 | "#BUILD", 16 | path.resolve(__dirname, "../Art Engine/build") 17 | ); 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | // ***** FOR LOCAL DEV: SET THE PATH TO YOUR BUILD FOLDER, RELATIVE TO THIS PROJECT ****// this path is 2 | // | 3 | // | 4 | // v 5 | const buildPath = "../Art Engine/build"; 6 | 7 | /** TO UPLOAD TO A LIVE SERVER, SET EMBED PATH TO TRUE */ 8 | const hosted = false; 9 | const hostedPath = "/build"; 10 | // set the image type of your images from your build folder 11 | const imageType = "png"; 12 | 13 | module.exports = { 14 | buildPath, 15 | imageType, 16 | hosted, 17 | hostedPath, 18 | }; 19 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "explorer", 3 | "version": "0.1.2", 4 | "private": true, 5 | "scripts": { 6 | "build": "vue-cli-service build", 7 | "lint": "vue-cli-service lint", 8 | "dev": "vue-cli-service serve" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.8.3", 12 | "sass": "^1.49.9", 13 | "sass-loader": "^12.6.0", 14 | "vue": "^3.2.13", 15 | "vuex": "^4.0.0" 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "^7.12.16", 19 | "@babel/eslint-parser": "^7.12.16", 20 | "@vue/cli-plugin-babel": "~5.0.0", 21 | "@vue/cli-plugin-eslint": "~5.0.0", 22 | "@vue/cli-plugin-vuex": "~5.0.0", 23 | "@vue/cli-service": "~5.0.0", 24 | "eslint": "^7.32.0", 25 | "eslint-plugin-vue": "^8.0.3" 26 | }, 27 | "eslintConfig": { 28 | "root": true, 29 | "env": { 30 | "node": true 31 | }, 32 | "extends": [ 33 | "plugin:vue/vue3-essential", 34 | "eslint:recommended" 35 | ], 36 | "parserOptions": { 37 | "parser": "@babel/eslint-parser" 38 | }, 39 | "rules": {} 40 | }, 41 | "browserslist": [ 42 | "> 1%", 43 | "last 2 versions", 44 | "not dead", 45 | "not ie 11" 46 | ] 47 | } -------------------------------------------------------------------------------- /src/components/ActiveFilters.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🧪 Art Engine Explorer (alpha) 2 | A local web app intended to be used in conjunction with the Art-Engine. This is a quick (desktop) viewer that allows you to visualize and 3 | 4 | image 5 | image 6 | 7 | # Controls 8 | Use `arrow keys` to scroll through items when the detail modal or fullscreen modes are open 9 | Use `Escape` to close teh detail modal or Fullscreen mode 10 | 11 | ## Roadmap 12 | [] Add rarity calculations 13 | [] Add "jump to number" input 14 | [] Integrate into the main art-engine repo as a submodule 15 | [] Properly configure the config.js file to allow for custom paths to the build dir 16 | 17 | # Project setup 18 | **⚠️ Important!!** 19 | - As of this writing, this repo should be installed _next_ to the Art Engine folder so the _Viewer_ project can easily the `build` folders from the Art Engine generator. 20 | - As of this writing, the viewer relies on the `.edition` metadata property for indexing images. 21 | 22 | 23 | Clone or Download this project _next to_ the generator folder. 24 | This build expects the art-engine folder to be named `Art Engine` 25 | [To adjust the path of the build folder, see vue.config.js AND App.vue in_this_project] 26 | 27 | If you are building JPEG images, change the config in `config.js` in the viewer config file. 28 | 29 | ``` 30 | yarn 31 | 32 | // or using npm 33 | npm install 34 | ``` 35 | 36 | ### Once installed, run `yarn dev` 37 | ``` 38 | yarn dev 39 | 40 | // if using npm 41 | // npm run dev 42 | ``` 43 | 44 | ## Building for production 45 | This application uses standard Vue-cli building and bundling. For more information on deploying a live version (After revealing all your NFT art of course), see the Vue.js documentation https://vuejs.org/guide/introduction.html 46 | ### Compiles and minifies for production 47 | ``` 48 | yarn build 49 | ``` 50 | 51 | ### Lints and fixes files 52 | ``` 53 | yarn lint 54 | ``` 55 | 56 | ### Customize configuration 57 | See [Configuration Reference](https://cli.vuejs.org/config/). 58 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 52 | 53 | 92 | -------------------------------------------------------------------------------- /src/components/DarkmodeToggle.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 47 | 48 | 102 | -------------------------------------------------------------------------------- /src/components/FilterAside.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 84 | 85 | 131 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore } from "vuex"; 2 | 3 | export default createStore({ 4 | state: { 5 | metadata: [], 6 | filters: [], 7 | results: [], 8 | allTraits: [], 9 | traitTypes: [], 10 | currentDetailIndex: 0, 11 | darkmode: false, 12 | }, 13 | getters: {}, 14 | mutations: { 15 | SET_RESULTS(state, _results) { 16 | state.results = _results; 17 | }, 18 | SET_FILTERS(state, _filters) { 19 | state.filters = _filters; 20 | }, 21 | SET_METADATA(state, _metadata) { 22 | state.metadata = _metadata; 23 | }, 24 | SET_TRAITS(state, _traitsData) { 25 | state.allTraits = _traitsData; 26 | }, 27 | SET_TRAIT_TYPES(state, _traits) { 28 | state.traitTypes = _traits; 29 | }, 30 | SET_CURRENT_INDEX(state, _index) { 31 | state.currentDetailIndex = _index; 32 | }, 33 | SET_DARKMODE(state, _on) { 34 | state.darkmode = _on; 35 | }, 36 | }, 37 | actions: { 38 | INITIALIZE_TRAITS({ commit }, metadata) { 39 | console.log("init traits,", metadata); 40 | commit("SET_METADATA", metadata); 41 | commit("SET_RESULTS", metadata); 42 | 43 | const allTraits = metadata.reduce((acc, item) => { 44 | // loop over each item in the entire metadata. this could be 45 | // time intensive. 46 | // Initialize the trait if it has not been initialized yet in the acc 47 | // initialize the value at count 1 it it has not been init yet 48 | // sum al the counts 49 | item.attributes.forEach((attribute) => { 50 | acc[attribute.trait_type] = { 51 | ...acc[attribute.trait_type], 52 | [attribute.value]: { 53 | filterState: false, 54 | count: acc[attribute.trait_type] 55 | ? acc[attribute.trait_type][attribute.value] 56 | ? acc[attribute.trait_type][attribute.value].count + 1 57 | : 1 58 | : 1, 59 | }, 60 | }; 61 | }); 62 | return acc; 63 | }, {}); 64 | 65 | const traitTypes = Object.keys(allTraits); 66 | commit("SET_TRAIT_TYPES", traitTypes); 67 | commit("SET_TRAITS", allTraits); 68 | }, 69 | 70 | SET_AVAILABLE_ATTRIBUTES({ commit, state }) { 71 | const allTraits = state.results.reduce((acc, item) => { 72 | // loop over each item in the entire metadata. this could be 73 | // time intensive. 74 | // Initialize the trait if it has not been initialized yet in the acc 75 | // initialize the value at count 1 it it has not been init yet 76 | // sum al the counts 77 | item.attributes.forEach((attribute) => { 78 | acc[attribute.trait_type] = { 79 | ...acc[attribute.trait_type], 80 | [attribute.value]: { 81 | filterState: false, 82 | count: acc[attribute.trait_type] 83 | ? acc[attribute.trait_type][attribute.value] 84 | ? acc[attribute.trait_type][attribute.value].count + 1 85 | : 1 86 | : 1, 87 | }, 88 | }; 89 | }); 90 | return acc; 91 | }, {}); 92 | 93 | commit("SET_TRAITS", allTraits); 94 | }, 95 | /** 96 | * 97 | * @param {context} param0 Unwraped context params for commit,state 98 | * @param {Array} filters [...{}] filters of types and value pairs 99 | */ 100 | UPDATE_RESULTS({ commit, state }, filters) { 101 | // TODO: do the filtering of the metadata here 102 | const subset = state.metadata.filter((item) => { 103 | return item.attributes.includes( 104 | (attribute) => 105 | attribute.trait_type === filters.trait_type && 106 | attribute.value === attribute.value 107 | ); 108 | }); 109 | 110 | commit("SET_RESULTS", subset); 111 | }, 112 | 113 | ADD_FILTER({ commit, dispatch, state }, filter) { 114 | const filters = [...state.filters, { ...filter, filterState: true }]; 115 | const updateState = { 116 | ...state.allTraits, 117 | }; 118 | updateState[filter.trait_type][filter.value].filterState = true; 119 | 120 | commit("SET_TRAITS", updateState); 121 | commit("SET_FILTERS", filters); 122 | dispatch("FILTER"); 123 | }, 124 | 125 | REMOVE_FILTER({ commit, dispatch, state }, traitFilter) { 126 | const filters = state.filters.filter( 127 | (trait) => 128 | trait.trait_type !== traitFilter.trait_type && 129 | trait.value !== traitFilter.value 130 | ); 131 | const updateState = { 132 | ...state.allTraits, 133 | }; 134 | updateState[traitFilter.trait_type][ 135 | traitFilter.value 136 | ].filterState = false; 137 | commit("SET_TRAITS", updateState); 138 | commit("SET_FILTERS", filters); 139 | dispatch("FILTER"); 140 | dispatch("SET_AVAILABLE_ATTRIBUTES"); 141 | }, 142 | 143 | FILTER({ commit, state, dispatch }) { 144 | if (!state.filters.length || state.filters.length === 0) { 145 | // clear all filter results and show all 146 | commit("SET_RESULTS", state.metadata); 147 | return; 148 | } 149 | 150 | // console.log("active filters", state.filters); 151 | 152 | const results = state.metadata.filter((item) => { 153 | return state.filters.every((filter) => { 154 | // if the items attributes match any of the filters, return true 155 | return item.attributes.some((attribute) => { 156 | return ( 157 | attribute.trait_type === filter.trait_type && 158 | attribute.value === filter.value 159 | ); 160 | }); 161 | }); 162 | 163 | // return matches.length > 0; 164 | }); 165 | // console.log({ results }); 166 | commit("SET_RESULTS", results); 167 | dispatch("SET_AVAILABLE_ATTRIBUTES"); 168 | }, 169 | 170 | // Set the metadata array index of the current selected item 171 | CURRENT_DETAIL_INDEX({ commit }, _index) { 172 | commit("SET_CURRENT_INDEX", _index); 173 | }, 174 | 175 | TOGGLE_DARK_MODE({ commit, state }) { 176 | commit("SET_DARKMODE", !state.darkmode); 177 | }, 178 | }, 179 | modules: {}, 180 | }); 181 | -------------------------------------------------------------------------------- /src/components/GalleryComponent.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 183 | 184 | 288 | --------------------------------------------------------------------------------