├── .eslintrc.json ├── .github └── workflows │ ├── docs-tests.yml │ ├── nftoken-js-tests.yml │ └── program-test.yml ├── .gitignore ├── .nvmrc ├── Anchor.toml ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE.md ├── README.md ├── docs ├── .eslintrc.json ├── .gitignore ├── @types │ └── styled-jsx.d.ts ├── README.md ├── components │ ├── all-pages │ │ ├── DesktopSecondaryNav.tsx │ │ ├── Footer.tsx │ │ ├── Header.tsx │ │ ├── MobileSecondaryNav.tsx │ │ ├── NextPreviousButtons.tsx │ │ ├── PageLayout.tsx │ │ ├── SocialHead.tsx │ │ └── navigation-constants.ts │ ├── atoms │ │ ├── ButtonSwitcher.tsx │ │ ├── DateTimePicker.tsx │ │ ├── ExternalLink.tsx │ │ ├── ImageCard.tsx │ │ ├── LuxButton.tsx │ │ ├── LuxDropZone.tsx │ │ ├── LuxEmptyState.tsx │ │ ├── LuxInput.tsx │ │ ├── LuxInputLabel.tsx │ │ ├── LuxLink.tsx │ │ ├── LuxMenu.tsx │ │ ├── LuxModal.tsx │ │ ├── LuxSimpleDropZone.tsx │ │ ├── LuxSpinner.tsx │ │ ├── NetworkContext.tsx │ │ ├── NetworkSwitcher.tsx │ │ ├── Shimmer.tsx │ │ ├── SolanaAddress.tsx │ │ ├── SquareImage.tsx │ │ ├── ValueList.tsx │ │ └── _LumaInputWrapper.tsx │ ├── forms │ │ ├── CsvDropZone.tsx │ │ ├── ImageDropZone.tsx │ │ └── SimpleDropZone.tsx │ ├── markdoc-atoms │ │ ├── AttributeTable.tsx │ │ ├── Callout.tsx │ │ ├── CodeBlock.tsx │ │ ├── Heading.tsx │ │ ├── LoomEmbed.tsx │ │ └── Picture.tsx │ ├── mintlist │ │ ├── CreateMintlistSection.tsx │ │ ├── MintInfosForm.tsx │ │ ├── MintlistForSale.tsx │ │ ├── MintlistInfoHeader.tsx │ │ ├── MintlistNftsGrid.tsx │ │ ├── MintlistPending.tsx │ │ ├── MintlistPresale.tsx │ │ ├── MintlistSaleEnded.tsx │ │ ├── MintlistStatusPill.tsx │ │ └── mintlist-utils.ts │ ├── nft │ │ ├── CreateNftSection.tsx │ │ └── InteractiveWell.tsx │ └── test │ │ └── ModalTest.tsx ├── hooks │ ├── useBoolean.ts │ ├── useCollectionNfts.ts │ ├── useIsMounted.ts │ ├── useOnMount.ts │ ├── usePersistedState.ts │ └── usePolling.tsx ├── icons │ ├── GitHub.svg │ ├── NftokenLogo.svg │ ├── Twitter.svg │ ├── TwitterIcon.tsx │ └── feather │ │ └── FileIcon.svg ├── markdoc │ ├── functions.ts │ ├── nodes.ts │ └── tags.ts ├── next-env.d.ts ├── next.config.js ├── package.json ├── pages │ ├── _app.tsx │ ├── _document.tsx │ ├── collection │ │ └── [collectionAddress].tsx │ ├── create-an-nft.tsx │ ├── docs │ │ ├── background.md │ │ ├── changelog.md │ │ ├── create-a-mintlist.md │ │ ├── creating-an-nft.md │ │ ├── faq.md │ │ ├── overview.md │ │ ├── security.md │ │ ├── selling-nfts.md │ │ └── technical-details.md │ ├── index.tsx │ ├── mintlist │ │ └── [mintlistAddress].tsx │ ├── mintlists.tsx │ ├── mintlists │ │ └── create.tsx │ ├── my-nfts.tsx │ ├── nft │ │ ├── [nftAddress].tsx │ │ └── create.tsx │ └── test │ │ └── modal.md ├── public │ ├── favicon.png │ ├── glow-logo-dark.svg │ ├── glow-logo-light.svg │ ├── logo-brand-color.svg │ ├── logo-light.svg │ ├── logo.svg │ ├── mintlist-states-dark.png │ ├── mintlist-states-light.png │ ├── mintlist-states.svg │ ├── nfts-metadata-template.csv │ ├── share.png │ └── sketch.png ├── styles │ └── app.scss ├── tsconfig.json ├── types │ └── markdoc.ts └── utils │ ├── NftokenTypes.ts │ ├── api-client.ts │ ├── cdn.ts │ ├── constants.ts │ ├── context.ts │ ├── create-axios-client.ts │ ├── email.ts │ ├── ethereum.ts │ ├── framer.ts │ ├── local-storage.ts │ ├── rpc-types.ts │ ├── social-links.ts │ ├── string.ts │ ├── style-constants.ts │ ├── timezone.ts │ ├── toast.ts │ └── upload-file.ts ├── nftoken-js ├── .eslintrc.json ├── .gitignore ├── .release-it.json ├── README.md ├── package.json ├── src │ ├── index.ts │ ├── mintlist.ts │ ├── nftoken-fetcher.ts │ ├── nftoken-formats.ts │ ├── nftoken-txs.ts │ └── nftoken-types.ts └── tsconfig.json ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── program-tests ├── @types │ └── buffer-layout.d.ts ├── collection-create.test.ts ├── collection-update.test.ts ├── ix-nft-burn.test.ts ├── ix-nft-transfer.test.ts ├── jest.config.js ├── mintlist-add-mint-infos.test.ts ├── mintlist-close.test.ts ├── mintlist-creation.test.ts ├── mintlist-mint-nft.test.ts ├── nft-create-v1.test.ts ├── nft-create-v2.test.ts ├── nft-delegation.test.ts ├── nft-setup-creators.test.ts ├── nft-update.test.ts ├── package.json ├── set-nft-collection.test.ts ├── tsconfig.json └── utils │ ├── IdlCoder.ts │ ├── KeypairWallet.ts │ ├── create-collection.ts │ ├── create-nft.ts │ ├── mintlist.ts │ └── test-utils.ts ├── programs └── nftoken │ ├── Cargo.toml │ ├── Xargo.toml │ └── src │ ├── account_types.rs │ ├── constants.rs │ ├── errors.rs │ ├── ix_collection_create.rs │ ├── ix_collection_update.rs │ ├── ix_collection_update_authority.rs │ ├── ix_mintlist_add_mint_infos.rs │ ├── ix_mintlist_close.rs │ ├── ix_mintlist_create.rs │ ├── ix_mintlist_mint_nft.rs │ ├── ix_nft_burn.rs │ ├── ix_nft_create_v1.rs │ ├── ix_nft_create_v2.rs │ ├── ix_nft_set_collection.rs │ ├── ix_nft_set_delegate.rs │ ├── ix_nft_setup_creators.rs │ ├── ix_nft_transfer.rs │ ├── ix_nft_unset_collection.rs │ ├── ix_nft_unset_delegate.rs │ ├── ix_nft_update.rs │ └── lib.rs ├── target └── idl │ └── nftoken.json └── turbo.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": ["@typescript-eslint"], 4 | "extends": [ 5 | "eslint:recommended", 6 | "plugin:@typescript-eslint/recommended", 7 | "prettier" 8 | ], 9 | "env": { 10 | "es6": true, 11 | "jest": true, 12 | "node": true 13 | }, 14 | "rules": { 15 | "curly": "error", 16 | "no-restricted-globals": ["error", "name", "event", "origin", "status"], 17 | "no-prototype-builtins": 0, 18 | "@typescript-eslint/ban-ts-comment": 0, 19 | "@typescript-eslint/explicit-function-return-type": 0, 20 | "@typescript-eslint/explicit-member-accessibility": 0, 21 | "@typescript-eslint/indent": 0, 22 | "@typescript-eslint/no-namespace": 0, 23 | "@typescript-eslint/member-delimiter-style": 0, 24 | "@typescript-eslint/no-explicit-any": 0, 25 | "@typescript-eslint/no-var-requires": 0, 26 | "@typescript-eslint/no-use-before-define": 0, 27 | "@typescript-eslint/no-non-null-assertion": 0, 28 | "@typescript-eslint/no-inferrable-types": 0, 29 | "@typescript-eslint/no-unused-vars": [ 30 | "error", 31 | { 32 | "argsIgnorePattern": "^_", 33 | "varsIgnorePattern": "^_", 34 | "ignoreRestSiblings": true 35 | } 36 | ], 37 | "no-constant-condition": 0, 38 | "prefer-const": [ 39 | "error", 40 | { 41 | "destructuring": "all" 42 | } 43 | ], 44 | "no-console": 0 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/docs-tests.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'docs/**' 7 | 8 | jobs: 9 | run_js_tests: 10 | name: Typescript Tests 11 | runs-on: ubuntu-20.04 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Install yarn dependencies 16 | run: npm install pnpm -g && pnpm install -r docs && pnpm run --filter nftoken-js build 17 | - name: Lint 18 | run: pnpm --filter docs run lint 19 | - name: Typescript Compile (tsc) 20 | run: pnpm --filter docs run tsc 21 | -------------------------------------------------------------------------------- /.github/workflows/nftoken-js-tests.yml: -------------------------------------------------------------------------------- 1 | name: nftoken-js 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'nftoken-js/**' 7 | 8 | jobs: 9 | run_nftoken_js_tests: 10 | name: Nftoken Tests 11 | runs-on: ubuntu-20.04 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Install yarn dependencies 16 | run: npm install pnpm -g && pnpm install --filter nftoken-js 17 | - name: Lint 18 | run: pnpm --filter nftoken-js run lint 19 | - name: Typescript Compile (tsc) 20 | run: pnpm --filter nftoken-js run tsc 21 | - name: Build 22 | run: pnpm --filter nftoken-js run build 23 | -------------------------------------------------------------------------------- /.github/workflows/program-test.yml: -------------------------------------------------------------------------------- 1 | name: Anchor Program 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'programs/**' 7 | - 'program-tests/**' 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | run_js_tests: 14 | name: Anchor Tests 15 | runs-on: ubuntu-20.04 16 | container: projectserum/build:v0.24.2 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: List rustup toolchains 21 | run: rustup toolchain list 22 | - name: Set default toolchain 23 | run: rustup default stable 24 | - name: List rustup toolchains 25 | run: rustup toolchain list 26 | - name: Generate new keygen 27 | run: solana-keygen new 28 | - name: Set solana target cluster to local 29 | run: solana config set --url http:localhost:8899 30 | - name: Check solana config 31 | run: solana config get 32 | - name: Install yarn dependencies 33 | run: npm install pnpm -g && pnpm install -r docs && pnpm run --filter nftoken-js build 34 | - name: Build 35 | run: anchor build 36 | - name: Lint 37 | run: pnpm --filter program-tests run lint 38 | - name: Typescript Compile (tsc) 39 | run: pnpm --filter program-tests run tsc 40 | - name: Run tests 41 | run: anchor test 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | 37 | .idea/ 38 | test-ledger/ 39 | target/ 40 | .anchor 41 | 42 | docs/.obsidian 43 | 44 | .turbo/ 45 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.15.1 2 | -------------------------------------------------------------------------------- /Anchor.toml: -------------------------------------------------------------------------------- 1 | [provider] 2 | cluster = "localnet" 3 | wallet = "~/.config/solana/id.json" 4 | 5 | [programs.localnet] 6 | nftoken = "nftokf9qcHSYkVSP3P2gUMmV6d4AwjMueXgUu43HyLL" 7 | 8 | [scripts] 9 | test = "pnpm --filter program-tests exec jest" 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions to this repo are welcome! 4 | 5 | ## Dev Environment 6 | 7 | You'll need to install a few things to get this running. This guide will assume you are using a Mac, but it probably applies to Linux. 8 | 9 | We use [Anchor](https://github.com/project-serum/anchor) to write this program. Anchor gives us a framework that makes it easier to write Solana programs. You can think of Anchor like the React of Solana Dev. Without Anchor, we have to write lower level code which is harder (like Vanilla JS in the React metaphor). 10 | 11 | ### Install Rust 12 | 13 | Solana and Anchor are written in and built with Rust. So we'll need to install Rust and Cargo which is the package manager for Rust. 14 | 15 | [Install Rust](https://www.rust-lang.org/tools/install) 16 | 17 | **Learning Rust** 18 | 19 | You actually don't need to know that much Rust in order to code Solana. But if you want to play around with it, [the Rust Book](https://doc.rust-lang.org/book/) and the [Rustlings](https://github.com/rust-lang/rustlings) projects are good resources. 20 | 21 | ### Install Solana 22 | 23 | You'll need to have Solana + Rust installed in order to run this locally. 24 | 25 | [Solana Docs](https://docs.solana.com/cli/install-solana-cli-tools) 26 | 27 | ### Install Anchor 28 | 29 | Anchor makes an `avm` CLI tool which makes it easy to upgrade Anchor versions on your machine. 30 | 31 | You can follow the [Anchor book instructions](https://book.anchor-lang.com/getting_started/installation.html) to install Anchor. 32 | 33 | ### Running a Local Solana Validator 34 | 35 | Once you have Solana installed, you can run a local validator on your machine. The local validator will have its own account state and you will deploy / test programs against this validator. 36 | 37 | I run my validator with this command: 38 | 39 | ```shell 40 | solana-test-validator -r 41 | ``` 42 | 43 | The `-r` flag will delete all of the previous state so you are starting fresh. 44 | 45 | Once you are running your local validator, you can see if your `solana` CLI tool is pointed at it by doing: 46 | 47 | ```shell 48 | solana config get 49 | ``` 50 | 51 | And if you want to see your current address or balance you can run: 52 | 53 | ```shell 54 | solana address 55 | solana balance 56 | ``` 57 | 58 | ### Running Tests 59 | 60 | When I am running the tests, I usually have a local validator running so that the test running doesn't need to start a validator. 61 | 62 | ```shell 63 | anchor test --provider.cluster localnet --skip-local-validator 64 | ``` 65 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "programs/*" 4 | ] 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © `2023` `Luma Labs, Inc.` 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NFToken 2 | 3 | NFToken is a simple, cheap standard for Solana. 4 | 5 | Docs: [nftoken.so](https://nftoken.so/overview) 6 | -------------------------------------------------------------------------------- /docs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": ["lodash"], 4 | "rules": { 5 | "curly": "error", 6 | "lodash/import-scope": [2, "method"], 7 | "no-restricted-globals": ["error", "name", "event", "origin", "status"], 8 | "prefer-const": [ 9 | "error", 10 | { 11 | "destructuring": "all" 12 | } 13 | ], 14 | "no-console": 0 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | *.log* 24 | 25 | # local env files 26 | .env*.local 27 | 28 | # vercel 29 | .vercel 30 | 31 | # typescript 32 | *.tsbuildinfo -------------------------------------------------------------------------------- /docs/@types/styled-jsx.d.ts: -------------------------------------------------------------------------------- 1 | import "react"; 2 | 3 | declare module "react" { 4 | interface StyleHTMLAttributes extends React.HTMLAttributes { 5 | jsx?: boolean; 6 | global?: boolean; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Docs for NFToken 2 | 3 | You can find them here: [nftoken.luma-dev.com](https://nftoken.luma-dev.com/) 4 | -------------------------------------------------------------------------------- /docs/components/all-pages/DesktopSecondaryNav.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { useRouter } from "next/router"; 3 | import { ResponsiveBreakpoint } from "../../utils/style-constants"; 4 | import { LuxLink } from "../atoms/LuxLink"; 5 | 6 | export type SecondaryNavLink = { title: string; href: string }; 7 | 8 | export function DesktopSecondaryNav({ 9 | links, 10 | }: { 11 | links: SecondaryNavLink[]; 12 | }) { 13 | const router = useRouter(); 14 | 15 | return ( 16 |
17 |
18 | {links.map((item) => { 19 | const active = router.pathname === item.href; 20 | 21 | return ( 22 |
23 | {active &&
} 24 | 30 | {item.title} 31 | 32 |
33 | ); 34 | })} 35 |
36 | 37 | 82 |
83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /docs/components/all-pages/Footer.tsx: -------------------------------------------------------------------------------- 1 | import TwitterIcon from "../../icons/Twitter.svg"; 2 | import GitHubIcon from "../../icons/GitHub.svg"; 3 | import { LuxButton } from "../atoms/LuxButton"; 4 | import { ResponsiveBreakpoint } from "../../utils/style-constants"; 5 | 6 | export const Footer = () => { 7 | return ( 8 |
9 |
10 | 11 | 12 | 13 | 14 | 15 |
16 | } 19 | href="https://github.com/glow-xyz/nftoken" 20 | iconPlacement="icon-only" 21 | color="primary" 22 | variant="link" 23 | rounded 24 | size="large" 25 | /> 26 | } 29 | href="https://twitter.com/glowwallet" 30 | iconPlacement="icon-only" 31 | color="twitter" 32 | variant="link" 33 | rounded 34 | size="large" 35 | /> 36 |
37 |
38 | 39 | 81 |
82 | ); 83 | }; 84 | -------------------------------------------------------------------------------- /docs/components/all-pages/MobileSecondaryNav.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { useRouter } from "next/router"; 3 | import { ResponsiveBreakpoint } from "../../utils/style-constants"; 4 | import { SecondaryNavLink } from "./DesktopSecondaryNav"; 5 | import { LuxLink } from "../atoms/LuxLink"; 6 | 7 | export function MobileSecondaryNav({ links }: { links: SecondaryNavLink[] }) { 8 | const router = useRouter(); 9 | return ( 10 |
11 |
12 | {links.map((item) => { 13 | return ( 14 |
15 | 21 | {item.title} 22 | 23 |
24 | ); 25 | })} 26 |
27 | 28 | 68 |
69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /docs/components/all-pages/NextPreviousButtons.tsx: -------------------------------------------------------------------------------- 1 | import ChevronLeftIcon from "@luma-team/lux-icons/feather/chevron-left.svg"; 2 | import ChevronRightIcon from "@luma-team/lux-icons/feather/chevron-right.svg"; 3 | import { useRouter } from "next/router"; 4 | import { LuxButton } from "../atoms/LuxButton"; 5 | import { DOC_PAGES } from "./navigation-constants"; 6 | 7 | export const NextPreviousButtons = () => { 8 | const router = useRouter(); 9 | 10 | const current = DOC_PAGES.find((item) => item.href === router.pathname); 11 | 12 | if (!current) { 13 | return null; 14 | } 15 | 16 | const index = DOC_PAGES.indexOf(current); 17 | 18 | return ( 19 |
20 | {DOC_PAGES[index - 1] ? ( 21 | } 24 | href={DOC_PAGES[index - 1].href} 25 | iconPlacement="left" 26 | rounded 27 | variant="link" 28 | color="brand" 29 | /> 30 | ) : ( 31 | // Spacer so the other link goes on the right. 32 |
33 | )} 34 | 35 | {DOC_PAGES[index + 1] && ( 36 | } 39 | href={DOC_PAGES[index + 1].href} 40 | iconPlacement="right" 41 | rounded 42 | variant="link" 43 | color="brand" 44 | /> 45 | )} 46 | 47 | 57 |
58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /docs/components/all-pages/PageLayout.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import React from "react"; 3 | import { ResponsiveBreakpoint } from "../../utils/style-constants"; 4 | import { Footer } from "./Footer"; 5 | import { Header } from "./Header"; 6 | import { DASHBOARD_PAGES, DOC_PAGES } from "./navigation-constants"; 7 | import { DesktopSecondaryNav } from "./DesktopSecondaryNav"; 8 | import { MobileSecondaryNav } from "./MobileSecondaryNav"; 9 | 10 | type SecondaryNavType = "docs" | "dashboard"; 11 | const SecondaryNavToLinks: Record< 12 | SecondaryNavType, 13 | Array<{ href: string; title: string }> 14 | > = { 15 | docs: DOC_PAGES, 16 | dashboard: DASHBOARD_PAGES, 17 | }; 18 | 19 | export const PageLayout = ({ 20 | children, 21 | secondaryNav, 22 | }: { 23 | children: React.ReactNode; 24 | secondaryNav: "docs" | "dashboard" | null; 25 | }) => { 26 | return ( 27 |
28 |
29 | 30 | {secondaryNav && ( 31 | 35 | )} 36 | 37 |
42 | {secondaryNav && ( 43 | 47 | )} 48 | 49 |
{children}
50 |
51 | 52 |
53 | 54 | 87 |
88 | ); 89 | }; 90 | -------------------------------------------------------------------------------- /docs/components/all-pages/SocialHead.tsx: -------------------------------------------------------------------------------- 1 | import Head from "next/head"; 2 | import React from "react"; 3 | import { Thing, WithContext } from "schema-dts"; 4 | 5 | export const SocialHead = ({ 6 | title: _title = "NFToken", 7 | subtitle, 8 | description = "A simple, inexpensive Solana NFT standard.", 9 | imagePath, 10 | imageUrl = "https://nftoken.so/share.png", 11 | imageSize = { width: 1200, height: 630 }, 12 | canonicalPath, 13 | canonicalUrl, 14 | blockRobots, 15 | schema, 16 | faviconUrl = '/favicon.png', 17 | children, 18 | twitterCardType = "summary_large_image", 19 | }: React.PropsWithChildren<{ 20 | title?: string; 21 | subtitle?: string; 22 | description?: string; 23 | imagePath?: string; 24 | imageUrl?: string | null; 25 | imageSize?: { height: number; width: number }; 26 | canonicalPath?: string; 27 | canonicalUrl?: string; 28 | blockRobots?: boolean; 29 | faviconUrl?: string; 30 | schema?: WithContext; 31 | twitterCardType?: "summary_large_image" | "summary"; 32 | }>) => { 33 | const title = subtitle ? `${subtitle} · ${_title}` : _title; 34 | return ( 35 | 36 | {title} 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {canonicalUrl ? ( 48 | <> 49 | 50 | 51 | 52 | ) : canonicalPath ? ( 53 | <> 54 | 58 | 59 | 60 | ) : null} 61 | 62 | {imageSize && ( 63 | <> 64 | 68 | 72 | 73 | )} 74 | 75 | {blockRobots && } 76 | 77 | {imagePath ? ( 78 | <> 79 | 83 | 87 | 88 | ) : imageUrl ? ( 89 | <> 90 | 91 | 92 | 93 | ) : null} 94 | 95 | {schema && ( 96 | 23 |