├── .babelrc ├── .env ├── .eslintrc.json ├── .github └── workflows │ └── cla.yml ├── .gitignore ├── .prettierrc ├── .tool-versions ├── .vscode ├── launch.json └── settings.json ├── CLA.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── TROUBLESHOOTING.md ├── app-firebase └── index.ts ├── components ├── ActionModal │ └── index.tsx ├── DestructiveModal │ └── index.tsx ├── DropsSubnav │ └── index.tsx ├── EmptyState │ └── index.tsx ├── FormDescription │ └── index.tsx ├── Forms │ ├── Artwork │ │ └── index.tsx │ ├── Collection │ │ └── index.tsx │ ├── Conflict │ │ └── index.tsx │ ├── Project │ │ └── index.tsx │ ├── TTForm │ │ └── index.tsx │ ├── Trait │ │ └── index.tsx │ ├── TraitSet │ │ └── index.tsx │ ├── TraitValue │ │ └── index.tsx │ ├── User │ │ └── index.tsx │ └── UserGroup │ │ └── index.tsx ├── Header │ └── index.tsx ├── HeaderWithAddButton │ └── index.tsx ├── Layout.tsx ├── MainNavItem │ └── index.tsx ├── MainNavProjectDropdown │ └── index.tsx ├── ProgressModal │ └── index.tsx ├── TimeframeDropdown │ └── index.tsx └── TraitValuesRow │ └── index.tsx ├── firebase.json ├── firestore.indexes.json ├── firestore.rules ├── functions ├── .gitignore ├── models │ ├── candymachine.ts │ ├── firebase.ts │ └── models.ts ├── package-lock.json ├── package.json ├── src │ ├── ArtworkGenerator.ts │ ├── CandyMachineDownloader.ts │ ├── httpFunctions │ │ └── index.ts │ └── index.ts └── tsconfig.json ├── models ├── api.tsx ├── collection.tsx ├── conflict.tsx ├── imageComposite.tsx ├── imageCompositeGroup.tsx ├── imageLayer.tsx ├── project.tsx ├── trait.tsx ├── traitSet.tsx ├── traitValue.tsx ├── user.tsx └── userGroup.tsx ├── next-env.d.ts ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── _app.tsx ├── index.tsx ├── projects │ ├── [projectId] │ │ ├── collections │ │ │ ├── [collectionId] │ │ │ │ ├── artwork │ │ │ │ │ ├── [artworkId] │ │ │ │ │ │ └── edit.tsx │ │ │ │ │ ├── create.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── composites │ │ │ │ │ ├── [compositeGroupId] │ │ │ │ │ │ ├── [compositeId] │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── playground.tsx │ │ │ │ │ │ └── rarity.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── conflicts │ │ │ │ │ ├── [conflictId] │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── create.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── edit.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── traitSets │ │ │ │ │ ├── [traitSetId] │ │ │ │ │ │ └── edit.tsx │ │ │ │ │ ├── create.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── traits │ │ │ │ │ ├── [traitId] │ │ │ │ │ ├── edit.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── values │ │ │ │ │ │ ├── [valueId] │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ ├── create-list.tsx │ │ │ │ │ │ ├── create.tsx │ │ │ │ │ │ └── import-list.tsx │ │ │ │ │ ├── create.tsx │ │ │ │ │ └── index.tsx │ │ │ └── create.tsx │ │ ├── edit.tsx │ │ └── index.tsx │ └── create.tsx └── usergroups │ ├── [groupId] │ ├── edit.tsx │ ├── index.tsx │ └── users │ │ ├── [userId] │ │ └── edit.tsx │ │ └── create.tsx │ ├── create.tsx │ └── index.tsx ├── postcss.config.js ├── signatures └── version1 │ └── cla.json ├── storage.rules ├── styles └── globals.css ├── tailwind.config.js ├── test-images ├── Curve Blue.png ├── Curve Neon Green.png ├── Curve Pinkish Red.png ├── Curve Royal Blue.png ├── Curve Yellow.png ├── Dot Bright Purple.png ├── Dot Light Blue.png ├── Dot Neon Bright Green.png ├── Dot Neon Yellow Green.png ├── Dot Orange.png ├── Dot Yellow.png ├── Squiggle Green.png ├── Squiggle Magenta.png ├── Squiggle Purple.png ├── Squiggle Royal Purple.png └── Squiggle Teal.png ├── test-trait-csvs ├── example-invalid-extra-newline.csv ├── example-invalid.csv └── example.csv ├── tsconfig.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | presets: ['next/babel'], 3 | plugins: [ 4 | 'superjson-next' // 👈 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_FIREBASE_API_KEY= 2 | NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN= 3 | NEXT_PUBLIC_FIREBASE_PROJECT_ID= 4 | NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET= 5 | NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID= 6 | NEXT_PUBLIC_FIREBASE_APP_ID= 7 | NEXT_PUBLIC_FIREBASE_FUNCTIONS_API_ENDPOINT= -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.github/workflows/cla.yml: -------------------------------------------------------------------------------- 1 | name: "CLA Assistant" 2 | on: 3 | issue_comment: 4 | types: [created] 5 | pull_request_target: 6 | types: [opened,closed,synchronize] 7 | 8 | jobs: 9 | CLAssistant: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: "CLA Assistant" 13 | if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' 14 | # Beta Release 15 | uses: cla-assistant/github-action@v2.1.3-beta 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | # the below token should have repo scope and must be manually added by you in the repository's secret 19 | PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }} 20 | with: 21 | path-to-signatures: 'signatures/version1/cla.json' 22 | path-to-document: 'https://github.com/theskeletoncrew/treat-toolbox/blob/main/CLA.md' # e.g. a CLA or a DCO document 23 | # branch should not be protected 24 | branch: 'main' 25 | allowlist: skeletoncrewrip,cosimo-rip 26 | 27 | #below are the optional inputs - If the optional inputs are not given, then default values will be taken 28 | #remote-organization-name: enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository) 29 | #remote-repository-name: enter the remote repository name where the signatures should be stored (Default is storing the signatures in the same repository) 30 | #create-file-commit-message: 'For example: Creating file for storing CLA Signatures' 31 | #signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo' 32 | #custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign' 33 | #custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA' 34 | #custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.' 35 | #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) 36 | #use-dco-flag: true - If you are using DCO instead of CLA 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # next.js 10 | /.next/ 11 | /out/ 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | *.pem 19 | 20 | # debug logs 21 | *-debug.log* 22 | 23 | # local env files 24 | .env.local 25 | .env.development.local 26 | .env.test.local 27 | .env.production.local 28 | 29 | # firebase config 30 | .firebaserc 31 | 32 | # vercel 33 | .vercel 34 | 35 | functions/backup/* 36 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "singleQuote": false 5 | } 6 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 14.17.5 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "type": "pwa-chrome", 10 | "request": "launch", 11 | "name": "Launch Chrome against localhost", 12 | "url": "http://localhost:3000/", 13 | "webRoot": "${workspaceFolder}" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true 4 | } -------------------------------------------------------------------------------- /CLA.md: -------------------------------------------------------------------------------- 1 | ### Treat Toolbox Individual Contributor License Agreement 2 | 3 | Thank you for your interest in contributing to open source software projects (“Projects”) made available 4 | by Treat Toolbox or its affiliates (“TTB”). This Individual Contributor License Agreement (“Agreement”) sets 5 | out the terms governing any source code, object code, bug fixes, configuration changes, tools, 6 | specifications, documentation, data, materials, feedback, information or other works of authorship that you 7 | submit or have submitted, in any form and in any manner, to TTB in respect of any of the Projects 8 | (collectively “Contributions”). If you have any questions respecting this Agreement, please contact 9 | help@treattoolbox.com. 10 | 11 | 12 | You agree that the following terms apply to all of your past, present and future Contributions. Except for 13 | the licenses granted in this Agreement, you retain all of your right, title and interest in and to your 14 | Contributions. 15 | 16 | 17 | **Copyright License.** You hereby grant, and agree to grant, to TTB a non-exclusive, perpetual, irrevocable, 18 | worldwide, fully-paid, royalty-free, transferable copyright license to reproduce, prepare derivative works 19 | of, publicly display, publicly perform, and distribute your Contributions and such derivative works, with 20 | the right to sublicense the foregoing rights through multiple tiers of sublicensees, including its use in 21 | commercial products. 22 | 23 | 24 | **Patent License.** You hereby grant, and agree to grant, to TTB a non-exclusive, perpetual, irrevocable, 25 | worldwide, fully-paid, royalty-free, transferable patent license to make, have made, use, offer to sell, sell, 26 | import, and otherwise transfer your Contributions, where such license applies only to those patent claims 27 | licensable by you that are necessarily infringed by your Contributions alone or by combination of your 28 | Contributions with the Project to which such Contributions were submitted, with the right to sublicense the 29 | foregoing rights through multiple tiers of sublicensees. 30 | 31 | 32 | **Moral Rights.** To the fullest extent permitted under applicable law, you hereby waive, and agree not to 33 | assert, all of your “moral rights” in or relating to your Contributions for the benefit of TTB, its assigns, and 34 | their respective direct and indirect sublicensees. 35 | 36 | 37 | **Third Party Content/Rights.** If your Contribution includes or is based on any source code, object code, bug 38 | fixes, configuration changes, tools, specifications, documentation, data, materials, feedback, information or 39 | other works of authorship that were not authored by you (“Third Party Content”) or if you are aware of any 40 | third party intellectual property or proprietary rights associated with your Contribution (“Third Party Rights”), 41 | then you agree to include with the submission of your Contribution full details respecting such Third Party 42 | Content and Third Party Rights, including, without limitation, identification of which aspects of your 43 | Contribution contain Third Party Content or are associated with Third Party Rights, the owner/author of the 44 | Third Party Content and Third Party Rights, where you obtained the Third Party Content, and any applicable 45 | third party license terms or restrictions respecting the Third Party Content and Third Party Rights. For greater 46 | certainty, the foregoing obligations respecting the identification of Third Party Content and Third Party Rights 47 | do not apply to any portion of a Project that is incorporated into your Contribution to that same Project. 48 | 49 | 50 | **Representations.** You represent that, other than the Third Party Content and Third Party Rights identified by 51 | you in accordance with this Agreement, you are the sole author of your Contributions and are legally entitled 52 | to grant the foregoing licenses and waivers in respect of your Contributions. If your Contributions were 53 | created in the course of your employment with your past or present employer(s), you represent that such 54 | employer(s) has authorized you to make your Contributions on behalf of such employer(s) or such employer 55 | (s) has waived all of their right, title or interest in or to your Contributions. 56 | 57 | 58 | **Disclaimer.** To the fullest extent permitted under applicable law, your Contributions are provided on an "as-is" 59 | basis, without any warranties or conditions, express or implied, including, without limitation, any implied 60 | warranties or conditions of non-infringement, merchantability or fitness for a particular purpose. You are not 61 | required to provide support for your Contributions, except to the extent you desire to provide support. 62 | 63 | 64 | **No Obligation.** You acknowledge that TTB is under no obligation to use or incorporate your Contributions 65 | into any of the Projects. The decision to use or incorporate your Contributions into any of the Projects will be 66 | made at the sole discretion of TTB or its authorized delegates .. 67 | 68 | 69 | **Disputes.** This Agreement shall be governed by and construed in accordance with the laws of the State of 70 | New York, United States of America, without giving effect to its principles or rules regarding conflicts of laws, 71 | other than such principles directing application of New York law. The parties hereby submit to venue in, and 72 | jurisdiction of the courts located in New York, New York for purposes relating to this Agreement. In the event 73 | that any of the provisions of this Agreement shall be held by a court or other tribunal of competent jurisdiction 74 | to be unenforceable, the remaining portions hereof shall remain in full force and effect. 75 | 76 | 77 | **Assignment.** You agree that TTB may assign this Agreement, and all of its rights, obligations and licenses 78 | hereunder. 79 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Welcome to the Treat Toolbox contributing guide 2 | 3 | Thank you for your interest in contributing to our project! 4 | 5 | In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR. 6 | 7 | ### Issues 8 | 9 | #### Create a new issue 10 | 11 | If you spot a problem with the docs, [search if an issue already exists](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments). If a related issue doesn't exist, you can open a new issue using a relevant [issue form](https://github.com/github/docs/issues/new/choose). 12 | 13 | #### Solve an issue 14 | 15 | Scan through our [existing issues](https://github.com/github/docs/issues) to find one that interests you. The [help wanted](https://github.com/theskeletoncrew/treat-toolbox/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) label is a good place to start. Feel free to open a PR for any issue you find to work on though! We welcome all contributions. 16 | 17 | ### Make Changes 18 | 19 | 1. Fork the repository (ex. [using the command line](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository)) 20 | 21 | 2. Install all dependencies as described in the [README](https://github.com/theskeletoncrew/treat-toolbox/blob/main/README.md) 22 | 23 | 3. Create a working branch and start with your changes! 24 | 25 | 4. Test your changes locally and ensure that they work as expected. 26 | 27 | 28 | ### Commit your update 29 | 30 | Commit the changes once you are happy with them. 31 | 32 | ### Pull Request 33 | 34 | When you're finished with the changes, create a pull request (PR). 35 | - Describe your changes thoroughly, so that reviewers understand your changes, the purpose of your pull request, and how to test that they are correct. 36 | - Don't forget to link your PR to an issue if you are solving one. 37 | - We may ask for changes to be made before a PR can be merged. You can make any other changes in your fork, then commit them to your branch. 38 | - As you update your PR and apply changes, mark each conversation as resolved. 39 | 40 | ### Your PR is merged! 41 | 42 | Congratulations :sparkles: Once your PR is merged, your contributions will be publicly visible. 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Github Social Image](https://user-images.githubusercontent.com/89373652/137255583-80d58aae-cc14-413e-bf80-2b619673fde2.png) 2 | 3 | [Treat Toolbox](https://treattoolbox.com) | Twitter [@TreatToolbox](https://twitter.com/TreatToolbox) | Discord: [Skeleton Crew](https://discord.gg/skeletoncrewrip) 4 | 5 | Feeling generous? Your contributions help fund future development. 6 | Send tips to our Solana wallet: CH6afYjjydFLPSrfQYEUNCdSNohLCAQV6ir6QnYeZU3t 7 | 8 | # About 9 | 10 | [Treat Toolbox](https://treattoolbox.com) is an open-source utility for managing the creation of generative-art NFT projects. The project was built by the team that created [The Skeleton Crew](https://skeletoncrew.rip) for use on their own project. 11 | 12 | Using Treat Toolbox, the final artwork for a project is created from a set of layered PNGs, each associated with user-defined "traits" and their specified rarities. The system is especially well-suited for [Solana](https://solana.com) based projects, as it is capable of exporting the artwork and metadata (PNG+JSON) pairs that are necessary for setting up [Metaplex's Candy Machine](https://github.com/metaplex-foundation/metaplex). 13 | 14 | Benefits of Treat Toolbox: 15 | 16 | - Fully web-based; no fumbling with shell scripts 17 | - Built to be usable even by non-technical team members 18 | - Handles Traits, Rarities, Artwork, and more 19 | - Eliminates potential for duplicates 20 | - Provides methods to avoid known conflicts in your art 21 | - Exports directly to PNG+JSON for use with Metaplex Candy Machine 22 | 23 | Today, the system is made to run locally, using an instance of the [Firebase emulator](https://firebase.google.com/docs/emulator-suite) as it's backing data and file storage. In the future, a hosted version may be made available. 24 | 25 | # Setup 26 | 27 | Prerequisites: 28 | 29 | - Your machine should have npm, nodejs v14, and the java runtime installed. 30 | 31 | First, you will need to get setup with Firebase, and install the Firebase emulator: 32 | 33 | 1. Install the [Firebase CLI](https://firebase.google.com/docs/cli) 34 | 35 | 2. Go to the [Firebase Console](https://console.firebase.google.com) and login. 36 | 37 | 3. Create a new project 38 | 39 | 4. Under Firestore Database click “Create database”. Start in Production Mode. Choose an appropriate location. 40 | 41 | 5. Go back to the "Project Overview" page, and under “Get Started by adding Firebase to your app”, click the icon named "Web". Register your app (hosting is not necessary). 42 | 43 | 6. Under “Use npm”, copy the keys provided for the firebaseConfig. Enter these in the appropriate places in the `.env` file included in Treat Toolbox. 44 | 45 | ``` 46 | NEXT_PUBLIC_FIREBASE_API_KEY=YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY 47 | NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=myproject.firebaseapp.com 48 | NEXT_PUBLIC_FIREBASE_PROJECT_ID=myproject 49 | NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=myproject.appspot.com 50 | NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=1111111111111 51 | NEXT_PUBLIC_FIREBASE_APP_ID=1:1111111111111:web:3333333333333333333333 52 | ``` 53 | 54 | 7. Go back to the "Functions" page, and click “Upgrade project”. Update your plan to get access to Firebase Functions (you wont be billed as long as you only use the emulators). 55 | 56 | 8. Run `firebase login` and follow the prompts to sign in. 57 | 58 | 9. Run `firebase init` in the root of the checked out repo. 59 | 60 | Use an existing project (the one you created in step 3). 61 | 62 | Use the answers below: 63 | ``` 64 | Which Firebase features do you want to set up for this directory? 65 | Choose Firestore, Functions, Storage, Emulators. 66 | 67 | What file should be used for Firestore Rules? 68 | firestore.rules [just press enter] 69 | 70 | File firestore.rules already exists. Do you want to overwrite it with the Firestore Rules from the Firebase Console? 71 | N [No] 72 | 73 | What file should be used for Firestore indexes? 74 | firestore.indexes.json [just press enter] 75 | 76 | File firestore.indexes.json already exists. Do you want to overwrite it with the Firestore Indexes from the Firebase Console? 77 | N [No] 78 | 79 | What language would you like to use to write Cloud Functions? 80 | Typescript 81 | 82 | Do you want to use ESLint to catch probable bugs and enforce style? 83 | n [No] 84 | 85 | File functions/package.json already exists. Overwrite? 86 | N [No] 87 | 88 | File functions/tsconfig.json already exists. Overwrite? 89 | N [No] 90 | 91 | File functions/src/index.ts already exists. Overwrite? 92 | N [Np] 93 | 94 | File functions/.gitignore already exists. Overwrite? 95 | N [No] 96 | 97 | Do you want to install dependencies with npm now? 98 | Y [Yes] 99 | 100 | What file should be used for Storage Rules? 101 | storage.rules [just press enter] 102 | 103 | File storage.rules already exists. Overwrite? 104 | N [No] 105 | 106 | Which Firebase emulators do you want to set up? 107 | Functions, Firestore, Storage 108 | 109 | Which port do you want to use for the functions emulator? 110 | 5001 [just press enter] 111 | 112 | Which port do you want to use for the firestore emulator? 113 | 8080 [just press enter] 114 | 115 | Which port do you want to use for the storage emulator? 116 | 9199 [just press enter] 117 | 118 | Would you like to enable the Emulator UI? 119 | Yes 120 | 121 | Which port do you want to use for the Emulator UI (leave empty to use any available port)? 122 | [just press enter] 123 | 124 | Would you like to download the emulators now? 125 | y [Yes] 126 | ``` 127 | 128 | 10. Next, start the emulators: 129 | 130 | ```bash 131 | npm install --prefix=functions 132 | # This builds the functions and starts the emulator set up to save to a folder called "backup" under the "functions" folder. 133 | npm run serve --prefix=functions 134 | ``` 135 | 136 | **_Notes_**: _If you have been using a older version of Treat Toolbox, your backup may be in a different location. You can move the folder under the `functions` folder when the emulator isn't running._ 137 | 138 | _If the emulators fail to start, see `firestore-debug.log`. One common cause for failure is that you may need to install the [java runtime](http://www.java.com.)_ 139 | 140 | 11. When the emulators start, the API url for your cloud functions will be output in the form: 141 | 142 | `functions[us-central1-api]: http function initialized (http://localhost:5001/projectdemo-a111a/us-central1/api).` 143 | 144 | Copy this url and enter it in the file `.env` for the value `NEXT_PUBLIC_FIREBASE_FUNCTIONS_API_ENDPOINT`. 145 | 146 | 12. Then, in a separate terminal inside root of the checked out repo, start the development web server: 147 | 148 | ```bash 149 | npm install 150 | npm run dev 151 | ``` 152 | 153 | 13. Open [http://localhost:3000](http://localhost:3000) with your browser and start using Treat Toolbox! 154 | 155 | # Working with Treat Toolbox 156 | 157 | Check out the [Product Documentation](https://docs.treattoolbox.com/) 158 | 159 | Here's a video of the basic usage of Treat Toolbox: https://vimeo.com/633276431 160 | 161 | ### Quick Start 162 | 163 | Want to give treat toolbox a spin, but dont have artwork handy? Take a look at the samples we provided in the `test-images` directory. Just create three traits: "Curve", "Dot" and "Squiggle". Give each of them trait values, for ex. for "Squiggle", create the values "Green", "Magenta", "Purple", "Royal Purple", and "Teal". Then with the rarities you specify, you can run off a set of compositions and see the system at work. 164 | 165 | ### Troubleshooting 166 | 167 | For now, Treat Toolbox is lacking robust error reporting. If something feels off, check for errors in either one of the two running terminals (the web frontend or the firebase emulators) or alternatively, open the web console from the browser and check for errors there. If you do run into a problem, please file an Issue, or better yet, open a PR! 168 | 169 | ### Made with Treat Toolbox 170 | 171 | Help us get the word out about this tool by using the banner provided below as a way to comply with MIT LICENSE attribution requirements. Let's make sure that every artist who wants to launch an NFT Collection on Solana has the means to do so! Please link the banner to https://treattoolbox.com. 172 | 173 | ![Treat Toolbox Badge](https://user-images.githubusercontent.com/89115113/137352273-c4972230-8239-45b7-bfe1-be1767fd1115.png) 174 | -------------------------------------------------------------------------------- /TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | Here's a list of common things to rule out when troubleshooting issues: 2 | - first, make sure you have carefully followed all steps in the README. 3 | - are you up to date on the latest git commit? 4 | - have you run npm install and npm build --prefix functions since the last time you pulled from git? 5 | - have you setup your .env file as described in the readme including the functions API endpoint? 6 | - is one and only one instance of your emulator running (on the ports expected)? 7 | - have you created a user group in treat toolbox? 8 | - have you setup traits, values for each trait, and artwork, and mapped each piece of artwork to a trait and trait value? 9 | - are the rarities of your trait values set between 0 and 1? 10 | - do you have enough trait values setup so that multiplying the number of trait values together for each trait exceeds your requested supply? (ideally *significantly* exceeds to account for things like conflict resolution) 11 | - are your art files all PNGs? 12 | - do you have adequate disk space to generate lots of PNGs? 13 | - what version of java are you running? (on mac have you tried OpenJDK through homebrew? 14 | -------------------------------------------------------------------------------- /app-firebase/index.ts: -------------------------------------------------------------------------------- 1 | import { getApps, initializeApp } from "firebase/app"; 2 | import { getAuth, connectAuthEmulator } from "firebase/auth"; 3 | import { getFirestore, connectFirestoreEmulator } from "firebase/firestore"; 4 | import { getStorage, connectStorageEmulator } from "firebase/storage"; 5 | 6 | const apps = getApps(); 7 | const isFirstInitialization = !apps.length; 8 | 9 | let firebase = !isFirstInitialization 10 | ? apps[0] 11 | : initializeApp({ 12 | apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, 13 | authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, 14 | projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, 15 | storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, 16 | messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, 17 | appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, 18 | }); 19 | 20 | const auth = getAuth(firebase); 21 | const db = getFirestore(firebase); 22 | const storage = getStorage(firebase); 23 | 24 | if (isFirstInitialization && process.env.NODE_ENV === "development") { 25 | connectAuthEmulator(auth, "http://localhost:9099"); 26 | connectFirestoreEmulator(db, "localhost", 8080); 27 | connectStorageEmulator(storage, "localhost", 9199); 28 | } 29 | 30 | export { firebase as default, auth, db, storage }; 31 | -------------------------------------------------------------------------------- /components/ActionModal/index.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment, useRef } from "react"; 2 | import { Dialog, Transition } from "@headlessui/react"; 3 | import { FolderDownloadIcon } from "@heroicons/react/outline"; 4 | import Link from "next/link"; 5 | 6 | interface Props { 7 | title: string; 8 | message: string; 9 | actionButtonTitle: string; 10 | cancelButtonTitle?: string; 11 | actionURLs: string[]; 12 | cancelAction: () => void; 13 | show?: boolean; 14 | } 15 | 16 | export const ActionModal: React.FC = ({ 17 | title, 18 | message, 19 | actionButtonTitle, 20 | cancelButtonTitle = "Cancel", 21 | actionURLs, 22 | cancelAction, 23 | show, 24 | }) => { 25 | const cancelButtonRef = useRef(null); 26 | 27 | return ( 28 | 29 | 35 |
36 | 45 | 46 | 47 | 48 | {/* This element is to trick the browser into centering the modal contents. */} 49 | 55 | 64 |
65 |
66 |
67 |
72 |
73 | 77 | {title} 78 | 79 |
80 |

{message}

81 |
82 | {actionURLs.length > 1 83 | ? actionURLs.map((url, i) => ( 84 |
85 | 86 | 87 | Download Batch {i} 88 | 89 | 90 |
91 | )) 92 | : ""} 93 |
94 |
95 |
96 | {actionURLs.length == 1 ? ( 97 | 98 | 105 | 106 | ) : ( 107 | "" 108 | )} 109 | 117 |
118 |
119 |
120 |
121 |
122 |
123 | ); 124 | }; 125 | -------------------------------------------------------------------------------- /components/DestructiveModal/index.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment, useRef } from 'react' 2 | import { Dialog, Transition } from '@headlessui/react' 3 | import { ExclamationIcon } from '@heroicons/react/outline' 4 | 5 | interface Props { 6 | title: string, 7 | message: string, 8 | deleteButtonTitle?: string, 9 | cancelButtonTitle?: string, 10 | deleteAction: (() => void), 11 | cancelAction: (() => void) 12 | show?: boolean 13 | } 14 | 15 | export const DestructiveModal: React.FC = ({ 16 | title, 17 | message, 18 | deleteButtonTitle = "Delete", 19 | cancelButtonTitle = "Cancel", 20 | deleteAction, 21 | cancelAction, 22 | show 23 | }) => { 24 | const cancelButtonRef = useRef(null) 25 | 26 | return ( 27 | 28 | 29 |
30 | 39 | 40 | 41 | 42 | {/* This element is to trick the browser into centering the modal contents. */} 43 | 46 | 55 |
56 |
57 |
58 |
60 |
61 | 62 | {title} 63 | 64 |
65 |

{message}

66 |
67 |
68 |
69 |
70 | 77 | 85 |
86 |
87 |
88 |
89 |
90 |
91 | ) 92 | } 93 | -------------------------------------------------------------------------------- /components/DropsSubnav/index.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/dist/client/link"; 2 | import Header from "../Header"; 3 | import Project from "../../models/project"; 4 | import Collection, { CollectionType } from "../../models/collection"; 5 | 6 | interface Tab { 7 | id: string; 8 | name: string; 9 | validTypes: CollectionType[]; 10 | } 11 | 12 | const tabs: Tab[] = [ 13 | { 14 | id: "details", 15 | name: "Details", 16 | validTypes: [ 17 | CollectionType.Generative, 18 | CollectionType.Prerendered, 19 | CollectionType.Tilemapped, 20 | ], 21 | }, 22 | { 23 | id: "traitSets", 24 | name: "Trait Sets", 25 | validTypes: [CollectionType.Generative, CollectionType.Tilemapped], 26 | }, 27 | { 28 | id: "traits", 29 | name: "Traits", 30 | validTypes: [CollectionType.Generative, CollectionType.Tilemapped], 31 | }, 32 | { 33 | id: "tilemaps", 34 | name: "Tile Maps", 35 | validTypes: [CollectionType.Tilemapped], 36 | }, 37 | { 38 | id: "artwork", 39 | name: "Artwork", 40 | validTypes: [ 41 | CollectionType.Generative, 42 | CollectionType.Prerendered, 43 | CollectionType.Tilemapped, 44 | ], 45 | }, 46 | { 47 | id: "conflicts", 48 | name: "Conflicts", 49 | validTypes: [CollectionType.Generative], 50 | }, 51 | { 52 | id: "composites", 53 | name: "Composites", 54 | validTypes: [ 55 | CollectionType.Generative, 56 | CollectionType.Prerendered, 57 | CollectionType.Tilemapped, 58 | ], 59 | }, 60 | ]; 61 | 62 | function classNames(...classes: any[]) { 63 | return classes.filter(Boolean).join(" "); 64 | } 65 | 66 | interface Props { 67 | project: Project; 68 | collection: Collection; 69 | section: string; 70 | } 71 | 72 | export default function DropsSubnav(props: Props) { 73 | const { project, collection, section } = props; 74 | 75 | const collectionType = collection.type ?? CollectionType.Generative; 76 | 77 | return ( 78 | <> 79 |
80 |
81 |
82 |
83 | 86 | 100 |
101 |
102 |
103 | 136 |
137 |
138 |
139 |
140 | 141 | ); 142 | } 143 | -------------------------------------------------------------------------------- /components/EmptyState/index.tsx: -------------------------------------------------------------------------------- 1 | import { PlusIcon } from "@heroicons/react/outline"; 2 | import { ReactNode } from "react"; 3 | 4 | interface Props { 5 | title: string; 6 | message: string; 7 | buttonTitle?: string | null; 8 | emptyIcon?: ReactNode | null; 9 | } 10 | 11 | export const EmptyState: React.FC = ({ 12 | title, 13 | message, 14 | buttonTitle, 15 | emptyIcon, 16 | }) => { 17 | return ( 18 |
19 |
20 | {emptyIcon ? ( 21 | emptyIcon 22 | ) : ( 23 | 38 | )} 39 |

{title}

40 |

{message}

41 | {buttonTitle ? ( 42 |
43 |
44 |
47 |
48 | ) : ( 49 | "" 50 | )} 51 |
52 |
53 | ); 54 | }; 55 | -------------------------------------------------------------------------------- /components/FormDescription/index.tsx: -------------------------------------------------------------------------------- 1 | export default function FormDescription(props: { 2 | children?: JSX.Element | JSX.Element[] | null; 3 | title: string; 4 | description: string; 5 | }) { 6 | const { children, title, description } = props; 7 | 8 | return ( 9 |
10 |
11 |

{title}

12 |

{description}

13 | {children} 14 |
15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /components/Forms/Project/index.tsx: -------------------------------------------------------------------------------- 1 | import { useRouter } from "next/router"; 2 | import { useForm } from "react-hook-form"; 3 | import Project, { Projects } from "../../../models/project"; 4 | import { TTForm } from "../TTForm"; 5 | import { yupResolver } from "@hookform/resolvers/yup"; 6 | import * as yup from "yup"; 7 | 8 | interface Props { 9 | isEdit?: boolean; 10 | project?: Project | null; 11 | } 12 | 13 | const schema = yup 14 | .object({ 15 | name: yup.string().trim().required("This field is required"), 16 | description: yup.string().trim().required("This field is required"), 17 | }) 18 | .required(); 19 | 20 | export const ProjectForm: React.FC = ({ 21 | isEdit = false, 22 | project = null, 23 | }) => { 24 | const { 25 | register, 26 | handleSubmit, 27 | formState: { errors }, 28 | } = useForm({ 29 | resolver: yupResolver(schema), 30 | defaultValues: project ?? {}, 31 | }); 32 | 33 | const router = useRouter(); 34 | const onSubmit = async (data: Project) => { 35 | data.url = "https://" + data.domain; 36 | 37 | if (isEdit) { 38 | if (!project) { 39 | return false; 40 | } 41 | await Projects.update(data, project.id); 42 | } else { 43 | await Projects.create(data); 44 | } 45 | 46 | router.push( 47 | { 48 | pathname: "/", 49 | query: {}, 50 | }, 51 | undefined, 52 | { shallow: false } 53 | ); 54 | 55 | return true; 56 | }; 57 | 58 | return ( 59 | 60 |
61 |
62 | 68 | 75 | {errors.name && ( 76 | {errors.name.message} 77 | )} 78 |
79 | 80 |
81 | 87 |
88 |