From 2c0f5318c028ff28402c8201a0328f090b71bc79 Mon Sep 17 00:00:00 2001 From: sohey Date: Thu, 8 Jan 2026 17:30:09 +0100 Subject: [PATCH 1/4] added initial claude --- .claude/commands/lint.md | 86 +++++++++++++++++++ claude.md | 30 +++++++ .../network-information/block-building.mdx | 4 +- .../transaction-finality.mdx | 2 + .../learn/hardhat/etherscan/etherscan-sbs.mdx | 16 ++++ .../hardhat-deploy/hardhat-deploy-sbs.mdx | 2 + .../hardhat-forking/hardhat-forking.mdx | 2 + .../hardhat-testing/hardhat-testing-sbs.mdx | 2 + .../analyzing-test-coverage.mdx | 2 + .../hardhat-verify/hardhat-verify-sbs.mdx | 6 ++ ...ase-using-privy-and-the-base-paymaster.mdx | 10 +++ .../gasless-transactions-with-paymaster.mdx | 16 +++- .../build-a-smart-wallet-funding-app.mdx | 6 ++ .../introduction-to-providers.mdx | 12 +++ .../useReadContract.mdx | 4 + .../nft-guides/complex-onchain-nfts.mdx | 8 ++ .../nft-guides/dynamic-nfts.mdx | 10 +++ .../nft-guides/simple-onchain-nfts.mdx | 2 + .../nft-guides/thirdweb-unreal-nft-items.mdx | 22 +++++ .../technical-guides/neynar-notifications.mdx | 8 ++ 20 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 .claude/commands/lint.md create mode 100644 claude.md diff --git a/.claude/commands/lint.md b/.claude/commands/lint.md new file mode 100644 index 000000000..86ad2f6a8 --- /dev/null +++ b/.claude/commands/lint.md @@ -0,0 +1,86 @@ +# Lint MDX Documentation + +Check MDX files for formatting, structure, and Mintlify component usage. + +Reference `mintlify-reference.md` for correct component syntax. + +## Scope + +**By default, only check changed files:** +1. Run `git diff --name-only HEAD` to get uncommitted changes +2. Run `git diff --name-only master...HEAD` to include committed changes on this branch +3. Filter to only `.mdx` files in `docs/` + +**If `$ARGUMENTS` is provided:** +- `all` — check all MDX files in `docs/` +- A specific path — check only that file/folder (e.g., `docs/api`) + +## Steps + +1. Determine which files to check (changed files, all, or specific path) +2. Check each file for the issues below +3. Report issues with file path and line number +4. Suggest fixes for each issue + +## Checks to perform + +### Frontmatter +- Every MDX file should have frontmatter with at least `title` and `description` +- Frontmatter must be valid YAML between `---` fences + +### Heading structure +- No skipped heading levels (e.g., h1 → h3) +- Only one h1 per page (or none if title is in frontmatter) + +### Code blocks +- All code blocks must specify a language (```javascript not just ```) +- Code inside `` blocks should have both language AND label (```javascript Node.js) + +### Mintlify components +Check for correct syntax on these components: + +**Callouts** — must be self-closing or have content: +- ``, ``, ``, ``, `` + +**Structural** — check for proper nesting: +- `` must contain `` children +- `` must contain `` children +- `` must contain `` children + +**Cards** — check required attributes: +- `` should have `title` and `href` +- `` should have `cols` attribute + +**API docs** — check required attributes: +- `` needs `path|body|query|header`, `type`, and ideally `required` or `default` +- `` needs `name` and `type` + +**Media**: +- Images should be wrapped in `` +- `` tags should have `alt` attributes + +### Common mistakes to flag +- Unclosed MDX components (e.g., `` without ``) +- Using HTML comments `` instead of MDX `{/* */}` +- Broken internal links (files that don't exist in docs/) +- Empty code blocks +- Components with typos (e.g., `` instead of ``) + +## Output format + +``` +## Lint Results + +### Files checked +- 3 changed files (use `/lint all` to check entire docs/) + +### ❌ Errors (must fix) +- `docs/getting-started.mdx:15` — Code block missing language specifier +- `docs/api/users.mdx:42` — Unclosed component + +### ⚠️ Warnings (should fix) +- `docs/guides/auth.mdx:8` — Image not wrapped in + +### ✅ Summary +- 3 files checked, 2 errors, 1 warning +``` diff --git a/claude.md b/claude.md new file mode 100644 index 000000000..68375ba4c --- /dev/null +++ b/claude.md @@ -0,0 +1,30 @@ +# Documentation Repository + +## Stack +- Mintlify for documentation site +- MDX files in `docs/` folder +- Storybook for component demos + +## Git +- Primary branch: `master` + +## Key References +- `mintlify-reference.md` — Mintlify component syntax and usage +- `content-instructions.md` — Content guidelines +- `global-tone-voice.mdx` — Tone and voice standards +- `docs.json` — Site navigation and configuration + +## Conventions +- Use American English spelling +- Sentence case for headings +- All images wrapped in `` +- Code blocks must specify a language +- Use Mintlify callouts sparingly (``, ``, ``, ``, ``) + +## Commands +- `/lint` — Check MDX formatting and Mintlify component usage + +## Before Committing +- Run `/lint` and fix any errors +- If removing docs, add redirects in `docs.json` +- Ensure all internal links are valid diff --git a/docs/base-chain/network-information/block-building.mdx b/docs/base-chain/network-information/block-building.mdx index df98d467e..99ec7866b 100644 --- a/docs/base-chain/network-information/block-building.mdx +++ b/docs/base-chain/network-information/block-building.mdx @@ -24,11 +24,11 @@ See the [Configuration Changelog](/base-chain/network-information/configuration- Currently, blocks are built using [op-rbuilder](https://github.com/flashbots/op-rbuilder) and priority fee auctions occur every 200ms. There are two changes from the vanilla ordering to be aware of: -##### Timing +#### Timing Flashblocks are built every 200ms, each ordering a portion of the block. Unlike the current system where later-arriving transactions with higher priority fees can be placed at the top of the block, Flashblocks creates a time-based constraint. Once a Flashblock is built and broadcast, its transaction ordering is locked even if a transaction with a higher priority fee arrives later, it cannot be included in earlier, already built Flashblocks. -##### High Gas Limits +#### High Gas Limits If your app creates transactions with large gas limits, we recommend monitoring to detect any changes in inclusion latency. Transactions with gas limits over 1/10 of the current block gas limit (currently 14 million gas), face additional constraints: diff --git a/docs/base-chain/network-information/transaction-finality.mdx b/docs/base-chain/network-information/transaction-finality.mdx index eed553521..01ae09508 100644 --- a/docs/base-chain/network-information/transaction-finality.mdx +++ b/docs/base-chain/network-information/transaction-finality.mdx @@ -19,7 +19,9 @@ This describes finality for transactions on Base except withdrawal transactions For transactions on Base, finality is not a single time to wait for. Instead, there are 4 stages in time that each provide increasing security guarantees. + ![Diagram of transaction finality stages on Base](/images/transaction-finality/base-tx-finality.jpg) + diff --git a/docs/learn/hardhat/etherscan/etherscan-sbs.mdx b/docs/learn/hardhat/etherscan/etherscan-sbs.mdx index a8517bfcb..399f2a443 100644 --- a/docs/learn/hardhat/etherscan/etherscan-sbs.mdx +++ b/docs/learn/hardhat/etherscan/etherscan-sbs.mdx @@ -24,7 +24,9 @@ By the end of this lesson, you should be able to: [Etherscan](https://etherscan.io) is a popular Blockchain explorer that works for several different networks. In it, you can explore the state and activity of a particular network. + ![Etherscan](/images/learn/etherscan/etherscan-user-interface.png) + You can explore: @@ -35,7 +37,9 @@ You can explore: For instance, the following shows the details of a Block: + ![Block](/images/learn/etherscan/blocks.png) + Where you see information such as: @@ -56,7 +60,9 @@ One of the things you can do with Etherscan is interact with already-deployed co For example, if you want to read information from a famous contract such as [BAYC](https://etherscan.io/token/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d), you can simply go to Etherscan and explore the contract: + ![BAYC](/images/learn/etherscan/bayc.png) + You are able to see information such as: @@ -68,19 +74,25 @@ You are able to see information such as: In the **Contract** tab, you can see the full source code of BAYC: + ![BAYC Verified](/images/learn/etherscan/bayc-verified.png) + For a developer, verifying contracts is important since it gives transparency to your users. However, there are some risks because this means that bad actors can see the full source code and can try to exploit it. In order to read the state of the BAYC, you can go to the main menu and select the option **Read Contract**: + ![BAYC Read](/images/learn/etherscan/bayc-read.png) + After you select that option, you are able to see all of the read functions of the contract. You can also query who is the owner of the BAYC with id 150: + ![BAYC Query](/images/learn/etherscan/bayc-query.png) + ## Writing data to smart contracts using Etherscan @@ -88,13 +100,17 @@ In a similar fashion, you can read data from smart contracts using Etherscan. It To write data, go to the **Write Contract** tab: + ![Write Contract](/images/learn/etherscan/bayc-write.png) + From there, connect your wallet by clicking the **Connect with web3** button. After you connect, the following UI appears: + ![Write BAYC Connected](/images/learn/etherscan/bayc-write-connected.png) + You can then call the functions you wish to write to. diff --git a/docs/learn/hardhat/hardhat-deploy/hardhat-deploy-sbs.mdx b/docs/learn/hardhat/hardhat-deploy/hardhat-deploy-sbs.mdx index 880b53292..63a82808a 100644 --- a/docs/learn/hardhat/hardhat-deploy/hardhat-deploy-sbs.mdx +++ b/docs/learn/hardhat/hardhat-deploy/hardhat-deploy-sbs.mdx @@ -235,7 +235,9 @@ npx hardhat deploy --network base_sepolia After you run the command, a deployments folder appears with a newly-created deployment for `base_sepolia`: + ![New deployment](/images/learn/hardhat-deploying/new-deploy.png) + If you want to deploy to another network, change the network name as follows: diff --git a/docs/learn/hardhat/hardhat-forking/hardhat-forking.mdx b/docs/learn/hardhat/hardhat-forking/hardhat-forking.mdx index 120159e05..ba9941cda 100644 --- a/docs/learn/hardhat/hardhat-forking/hardhat-forking.mdx +++ b/docs/learn/hardhat/hardhat-forking/hardhat-forking.mdx @@ -145,7 +145,9 @@ describe('BalanceReader tests', () => { In this example, the [USDC address](https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48) is used and since USDC is an ERC-20 token, you can explore the token holders of that particular token directly in Etherscan: + ![Hardhat forking](/images/learn/hardhat-forking/hardhat-forking.png) + Or, visit https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#balances, where you can see, at the time or writing, Arbitrum ONE Gateway is the top token holder. diff --git a/docs/learn/hardhat/hardhat-testing/hardhat-testing-sbs.mdx b/docs/learn/hardhat/hardhat-testing/hardhat-testing-sbs.mdx index 7fe366d15..804a4578a 100644 --- a/docs/learn/hardhat/hardhat-testing/hardhat-testing-sbs.mdx +++ b/docs/learn/hardhat/hardhat-testing/hardhat-testing-sbs.mdx @@ -165,7 +165,9 @@ it('should get the unlockTime value', async () => { Notice how autocomplete appears after entering `lockInstance`: + ![Auto complete](/images/learn/hardhat-testing/autocomplete-unlockTime.png) + You can simply run `npx hardhat test` and then get: diff --git a/docs/learn/hardhat/hardhat-tools-and-testing/analyzing-test-coverage.mdx b/docs/learn/hardhat/hardhat-tools-and-testing/analyzing-test-coverage.mdx index d04ade8c7..28ce7bd88 100644 --- a/docs/learn/hardhat/hardhat-tools-and-testing/analyzing-test-coverage.mdx +++ b/docs/learn/hardhat/hardhat-tools-and-testing/analyzing-test-coverage.mdx @@ -176,7 +176,9 @@ All files | 100 | 83.33 | 100 | 100 | | Which then gives you a report of the test coverage of your test suite. Notice there is a new folder called `coverage`, which was generated by the `solidity-coverage` plugin. Inside the `coverage` folder there is a `index.html` file. Open it in a browser, you'll see a report similar to the following: + ![Coverage report](/images/hardhat-test-coverage/coverage-report.png) + ## Increasing test coverage diff --git a/docs/learn/hardhat/hardhat-verify/hardhat-verify-sbs.mdx b/docs/learn/hardhat/hardhat-verify/hardhat-verify-sbs.mdx index ba738cd8c..bc9f5b70e 100644 --- a/docs/learn/hardhat/hardhat-verify/hardhat-verify-sbs.mdx +++ b/docs/learn/hardhat/hardhat-verify/hardhat-verify-sbs.mdx @@ -28,7 +28,9 @@ The way smart contracts are verified is by simply uploading the source code and Once the contract is verified, the Etherscan explorer shows a status like the following image: + ![Verified contract](https://github.com/base/web/blob/master/apps/base-docs/docs/public/images/learn/hardhat-verify/hardhat-verify.png) + Luckily, Hardhat and Hardhat-deploy already contain a built-in capability to do this task easily on your behalf. @@ -44,7 +46,9 @@ In order to obtain an Etherscan API key, visit [Etherscan](https://etherscan.io/ Then, go to [https://etherscan.io/myapikey](https://etherscan.io/myapikey) and create an API key by clicking the **Add** button: + ![Add key](https://github.com/base/web/blob/master/apps/base-docs/docs/public/images/learn/hardhat-verify/harhat-verify-create-key.png) + Bear in mind that different networks have other Blockchain explorers. For example: @@ -96,7 +100,9 @@ waiting for result... You can now go to Basescan and search for your contract address, where you'll see the following: + ![Base scan success](/images/learn/hardhat-verify/hardhat-verify-success.png) + ## Conclusion diff --git a/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-privy-and-the-base-paymaster.mdx b/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-privy-and-the-base-paymaster.mdx index 544a36336..5f86a1aa9 100644 --- a/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-privy-and-the-base-paymaster.mdx +++ b/docs/learn/onchain-app-development/account-abstraction/account-abstraction-on-base-using-privy-and-the-base-paymaster.mdx @@ -229,21 +229,29 @@ Finally, run `yarn dev` and navigate to [http://localhost:3000] to see the start Before exploring the code, test the app. First, you should see this login page: + ![Privy Login Page](/images/account-abstraction/privy-login-page.png) + After clicking "Log in" you'll see the following modal: + ![Privy Login Modal](/images/account-abstraction/privy-login-modal.png) + By default, you can login with a wallet, or email. After logging in, you'll be redirected to the `/dashboard` page, where the demo app will allow you to connect a number of other accounts to your `user` object: + ![Privy Dashboard Page](/images/account-abstraction/privy-dashboard-page.png) + If you navigate to [console.privy.io](https://console.privy.io/), you'll see that Privy stores all your users and their data here. + ![Privy Console](/images/account-abstraction/privy-console.png) + ### PrivyProvider @@ -278,7 +286,9 @@ Add a `config` property to the `` in `_app.jsx` with `'github'` Refresh to see that authentication is only possible now through Github or SMS: + ![Privy Login Methods](/images/account-abstraction/privy-login-methods.png) + You can find a full list of `loginMethods` in the docs for [`PrivyClientConfig`]. diff --git a/docs/learn/onchain-app-development/account-abstraction/gasless-transactions-with-paymaster.mdx b/docs/learn/onchain-app-development/account-abstraction/gasless-transactions-with-paymaster.mdx index 1023bdc51..b3a34b273 100644 --- a/docs/learn/onchain-app-development/account-abstraction/gasless-transactions-with-paymaster.mdx +++ b/docs/learn/onchain-app-development/account-abstraction/gasless-transactions-with-paymaster.mdx @@ -44,14 +44,20 @@ In this section, you will configure a Paymaster to sponsor payments on behalf of ### Screenshots -- **Selecting your project** +- **Selecting your project** + ![cdp-home.png](/images/gasless-transaction-on-base/cdp-select-project.png) + -- **Navigating to the Paymaster tool** +- **Navigating to the Paymaster tool** + ![cdp-paymaster-tool.png](/images/gasless-transaction-on-base/cdp-paymaster.png) + -- **Configuration screen** +- **Configuration screen** + ![cdp-paymaster-tool.png](/images/gasless-transaction-on-base/cdp-config.png) + ### Allowlist a Sponsorable Contract @@ -60,7 +66,9 @@ In this section, you will configure a Paymaster to sponsor payments on behalf of 3. Click **Add** to add an allowlisted contract. 4. For this example, add [`0x83bd615eb93eE1336acA53e185b03B54fF4A17e8`][simple NFT contract], and add the function `mintTo(address)`. + ![cdp-allowlist-contracts.png](/images/gasless-transaction-on-base/cdp-allowlist-contract.png) + > **Use your own contract** > We use a [simple NFT contract][simple NFT contract] on Base mainnet as an example. Feel free to substitute your own. @@ -84,7 +92,9 @@ This means **each user** can only have \$0.05 in sponsored gas and **1** user op Next, **Set the Global Limit**. For example, set this to `$0.07` so that once the entire paymaster has sponsored \$0.07 worth of gas (across all users), no more sponsorship occurs unless you raise the limit. + ![cdp-global-user-limits.png](/images/gasless-transaction-on-base/cdp-global-user-limits.png) + ## Test Your Paymaster Policy diff --git a/docs/learn/onchain-app-development/finance/build-a-smart-wallet-funding-app.mdx b/docs/learn/onchain-app-development/finance/build-a-smart-wallet-funding-app.mdx index 72772d3d6..e5323108c 100644 --- a/docs/learn/onchain-app-development/finance/build-a-smart-wallet-funding-app.mdx +++ b/docs/learn/onchain-app-development/finance/build-a-smart-wallet-funding-app.mdx @@ -36,7 +36,9 @@ You'll need to set up an account on with [Coinbase Developer Platform (CDP) Acco If you see a "something went wrong" error message when navigating to pay.coinbase.com, make sure you have "enforce secure initialization" disabled on the [Onramp config page] in Coinbase Developer Platform Dashboard. + ![fund-onramp-config](/images/onchainkit-tutorials/fund-onramp-config.png) + ## Setting up the Project @@ -113,7 +115,9 @@ Now that we know the user's balance, we can then have them mint an NFT or prompt The end state is to show their balance along with the appropriate call to actions like so: + ![fund-wallet](/images/onchainkit-tutorials/fund-wallet-balance.png) + Update your component's return statement with the following code: @@ -146,7 +150,9 @@ return ( Sweet! Now our conditional rendering is in full force. If a user clicks on the `+ Add funds to transact` button they will be given three options for topping up their smart wallet: + ![fund-wallet](/images/onchainkit-tutorials/fund-funding-options.png) + ## Conclusion diff --git a/docs/learn/onchain-app-development/frontend-setup/introduction-to-providers.mdx b/docs/learn/onchain-app-development/frontend-setup/introduction-to-providers.mdx index 6460ce71a..80ef241c9 100644 --- a/docs/learn/onchain-app-development/frontend-setup/introduction-to-providers.mdx +++ b/docs/learn/onchain-app-development/frontend-setup/introduction-to-providers.mdx @@ -133,11 +133,15 @@ Open up the [WalletConnect] homepage, and create an account, and/or sign in usin Click the `Create` button in the upper right of the `Projects` tab. + ![Create Button](/images/connecting-to-the-blockchain/wallet-connect-create-button.png) + Enter a name for your project, select the `App` option, and click `Create`. + ![Project Information](/images/connecting-to-the-blockchain/add-project-information.png) + Copy the _Project ID_ from the project information page, and paste it in as the `projectId` in `getDefaultWallets`. @@ -176,11 +180,15 @@ yarn run dev Click the `Connect Wallet` button, select your wallet from the modal, approve the connection, and you should see your network, token balance, and address or ENS name at the top of the screen. Select your wallet from the modal. + ![RainbowKit modal](/images/connecting-to-the-blockchain/rainbowkit-modal.png) + You've connected with the Public Provider! + ![Connected](/images/connecting-to-the-blockchain/connected.png) + ### QuickNode @@ -198,7 +206,9 @@ You'll need an RPC URL, so open up [QuickNode]'s site and sign up for an account On the next screen, you'll be asked to select a chain. Each endpoint only works for one. Select `Base`, click `Continue`. + ![Select Chain](/images/connecting-to-the-blockchain/quicknode-select-chain.png) + For now, pick `Base Mainnet`, but you'll probably want to delete this endpoint and create a new one for Sepolia when you start building. The free tier only allows you to have one at a time. @@ -229,7 +239,9 @@ To test this out, switch networks a few times. You'll know it's working if you s [Alchemy] is [no longer baked into wagmi], but it still works the same as any other RPC provider. As with QuickNode, you'll need an account and a key. Create an account and/or sign in, navigate to the `Apps` section in the left sidebar, and click `Create new app`. + ![Alchemy new app](/images/connecting-to-the-blockchain/alchemy-new-app.png) + Select Base Mainnet, and give your app a name. diff --git a/docs/learn/onchain-app-development/reading-and-displaying-data/useReadContract.mdx b/docs/learn/onchain-app-development/reading-and-displaying-data/useReadContract.mdx index b0dbd3b39..52e36ed11 100644 --- a/docs/learn/onchain-app-development/reading-and-displaying-data/useReadContract.mdx +++ b/docs/learn/onchain-app-development/reading-and-displaying-data/useReadContract.mdx @@ -174,7 +174,9 @@ Add in instance of your new component to `index.tsx`: Run your app, and you should see your list of issues fetched from the blockchain and displayed in the console! + ![Issues Console Log](/images/learn/reading-and-displaying-data/issues-console-log.png) + Breaking down the hook, you've: @@ -261,7 +263,9 @@ useEffect(() => { Everything appears to be working just fine, but how is `issueOne.desc` undefined? You can see it right there in the log! + ![Missing Data](/images/learn/reading-and-displaying-data/missing-data.png) + If you look closely, you'll see that `voters` is missing from the data in the logs. What's happening is that because the nested `mapping` cannot be returned outside the blockchain, it simply isn't. TypeScript then gets the `data` and does the best it can to cast it `as` an `Issue`. Since `voters` is missing, this will fail and it instead does the JavaScript trick of simply tacking on the extra properties onto the object. diff --git a/docs/learn/token-development/nft-guides/complex-onchain-nfts.mdx b/docs/learn/token-development/nft-guides/complex-onchain-nfts.mdx index f8747cc74..6b3d9bbf9 100644 --- a/docs/learn/token-development/nft-guides/complex-onchain-nfts.mdx +++ b/docs/learn/token-development/nft-guides/complex-onchain-nfts.mdx @@ -50,7 +50,9 @@ You can also work from ours: [Sample Art] Either way, you should end up with something similar to this: + ![Mockup](/images/onchain-generative-nfts/mockup.png) + ## The Art of Making it Fit @@ -74,7 +76,9 @@ If you don't have the tools to do this, you can find these files here: [Sample A You'll need to build and deploy a number of contracts for this project. They'll be organized in this architecture: + ![Architecture](/images/onchain-generative-nfts/architecture.png) + Deploying this many contracts will have a cost associated with it, but once they're deployed, this contract will cost the same as any other NFT contract. Remember, `pure` and `view` functions called outside the blockchain don't cost any gas. This means that you can use multiple contracts to assemble a relatively large graphic without additional costs! @@ -406,7 +410,9 @@ Open the contract in [Basescan], connect with your wallet, and mint some NFTs. **Wait a few minutes**, then open the [testnet version of Opensea] and look up your contract. It may take several minutes to show up, but when it does, if everything is working you'll see NFTs with the ocean part of the art! Neat! + ![First pass NFT](/images/onchain-generative-nfts/first_pass.png) + ## Adding the Sky Renderer @@ -761,7 +767,9 @@ const SVGRenderer = await deploy('SVGRenderer', { Test as before. It's starting to look really nice! + ![Progress](/images/onchain-generative-nfts/progress.png) + ## Adding the Sun Renderer diff --git a/docs/learn/token-development/nft-guides/dynamic-nfts.mdx b/docs/learn/token-development/nft-guides/dynamic-nfts.mdx index ac68e287e..fc1212449 100644 --- a/docs/learn/token-development/nft-guides/dynamic-nfts.mdx +++ b/docs/learn/token-development/nft-guides/dynamic-nfts.mdx @@ -13,7 +13,9 @@ hide_table_of_contents: false In this tutorial, you will create a dynamic NFT using Irys's [mutability features]. + ![Overview](/images/dynamic-nfts/all-characters.png) + Dynamic NFTs are NFTs whose metadata evolves over time. They are commonly used in: @@ -54,7 +56,9 @@ Irys has a pay-once-store-forever model and accepts payment for storage using mu Data on Irys is permanent and immutable, but you use Irys's [mutability features] to simulate mutability and create dynamic NFTs that evolve based on onchain or offchain actions. + ![Overview](/images/dynamic-nfts/mutable-references.png) + Using Irys's mutability features, you create a single, static URL that is linked to a series of transactions. Then, you can add a new transaction to the series at any time, and the URL will always resolve to the most recent transaction. @@ -127,7 +131,9 @@ irys -w -t base-eth ## Uploading the images + ![All NFTs](/images/dynamic-nfts/all-characters.png) + [Download a zip containing PNGs] for each level, and save them on your local drive. @@ -241,7 +247,9 @@ To mint your NFT in Remix: 3. Under the `Mint` function, enter the wallet address you want to mint the NFT to and the metadata URL (e.g. `https://gateway.irys.xyz/mutable/94TNg3UUKyZ96Dj8eSo9DVkBiivAz9jT39jjMFeTFvm3`) from the previous step. 4. Click Transact. + ![Image Level 3](/images/dynamic-nfts/open-sea-mockup.jpg) + You can now view the NFT on the [Opensea Testnet]. @@ -260,7 +268,9 @@ irys upload metadata-level-2.json \ Return to Opensea and request that it refresh your metadata. + ![Image Level 3](/images/dynamic-nfts/refresh-metadata.png) + Give it a few minutes and your updated NFT should be visible. diff --git a/docs/learn/token-development/nft-guides/simple-onchain-nfts.mdx b/docs/learn/token-development/nft-guides/simple-onchain-nfts.mdx index 15ec666bb..0b91ad887 100644 --- a/docs/learn/token-development/nft-guides/simple-onchain-nfts.mdx +++ b/docs/learn/token-development/nft-guides/simple-onchain-nfts.mdx @@ -288,7 +288,9 @@ Write some local tests, then [deploy] and test your contract. It can be very tri Remember, it can take a few minutes for them to register and add the collection. If the metadata or image don't show up correctly, use [Sepolia Basescan] to pull the `tokenURI` and an online or console base64 decoder to decode and check the json metadata and SVG image. + ![Random Color NFT](/images/smart-wallet/random-color-nft.png) + ## Conclusion diff --git a/docs/learn/token-development/nft-guides/thirdweb-unreal-nft-items.mdx b/docs/learn/token-development/nft-guides/thirdweb-unreal-nft-items.mdx index b30283240..5a2841112 100644 --- a/docs/learn/token-development/nft-guides/thirdweb-unreal-nft-items.mdx +++ b/docs/learn/token-development/nft-guides/thirdweb-unreal-nft-items.mdx @@ -11,7 +11,9 @@ author: briandoyle81 In this tutorial, you'll learn how to add NFT item usage on top of the demo game you build in their [Unreal Engine Quickstart]. Specifically, you'll use an NFT collection of random colors to change the color of the player's race car. + ![Color changing car nft](/images/build-with-thirdweb/car-color-nft.gif) + ## Objectives @@ -97,7 +99,9 @@ We do not have an official browser recommendation, but during our testing, Chrom Navigate to the [thirdweb engine dashboard], and click the `Import` button. Enter a name and the local address for your engine instance: + ![Add engine instance](/images/build-with-thirdweb/import-image-instance.png) + Next, you must add your wallet to the engine instance. Open up the instance in the dashboard, then click the `Import` button next to `Backend Wallets`. Enter your secret key for the wallet. @@ -175,7 +179,9 @@ If later in the tutorial, you get an error when you attempt to claim a token, bu Copy the address from the dashboard: + ![Token Airdrop Dashboard](/images/build-with-thirdweb/token-airdrop-dashboard.png) + Return to the `.env` for your server, and add: @@ -190,21 +196,29 @@ Run the client and server with `yarn client` and `yarn server`. Navigate to `loc Clone the thirdweb [Unreal Demo], and open it with the Unreal Editor. Do so by clicking the `Recent Projects` tab in the upper left, then `Browse`, in the lower right. + ![Open Unreal Project](/images/build-with-thirdweb/open-unreal-project.png) + Open the folder cloned from the repo and select `unreal_demo.uproject`. You may need to convert the project to the current version of Unreal. Click the `Open a copy` button. When the scene loads, double-click `Scene_Game` in the upper-right corner. + ![Scene Game](/images/build-with-thirdweb/scene-game.png) + Before you can play, you need to do some config. Scroll down in the `Outliner` until you find `ThirdWebManager`. Click the `Open Thirdweb Manager` button to open the file in your editor. + ![Open Thirdweb Manager](/images/build-with-thirdweb/open-thirdweb-manager.png) + Then, click the green play button at the top of the viewport. + ![Play Button](/images/build-with-thirdweb/play-button.png) + Log in using the credentials you created on the website, and play the game for a minute or two. If you get a 404, check that your engine, client, and server are all still running. @@ -295,7 +309,9 @@ router.post('/claim-random-color-nft', claimRandomColorNFT); Return to the Unreal Editor and open `ThirdwebManager.cpp`: + ![Open ThirdwebManager.cpp](/images/build-with-thirdweb/open-thirdweb-manager.png). + Similarly to what you did in the server, use the existing `PerformClaim()` as a template to add a function for `PerformNFTClaim()`. The only thing different is the name of the function and the URL: @@ -325,7 +341,9 @@ Open the `Content Drawer` at the bottom, search for `CollectibleNFT`, and drag o Find the `Perform Claim` function call and replace it with `Perform NFT Claim`. **Note** that the `Target` is passed from `Get Actor of Class`. + ![Perform NFT Claim](/images/build-with-thirdweb/perform-nft-claim.png) + You'll want to be able to tell this collectible apart, so click on the mesh for `Collectible` on the left side in the `Component` tree, then on the `Details` panel on the right, find the `Materials` section and change it to `MI_Solid_Blue`. @@ -563,7 +581,9 @@ Finally, drag off `Bind Event to OnNFTColorsResponse` and add a `Set Timer by Fu You should end up with something like this: + ![Get NFT Colors](/images/build-with-thirdweb/get-nft-colors.png) + Compile the blueprint then run the game. You should see that last color in the array in the HUD, and you should see the full list printed in the console every two seconds. @@ -607,7 +627,9 @@ Return to `Canvas_HUD` and open the `Graph`. Drag out of the `SetText` node that Finally, add a `Set Vector Parameter Value`. Select `NFT_MPS` for the collection and `Vector` for the `Parameter Name`. Connect the `Liner Color` output of `Hex String to Color` to the `Parameter Value` input. + ![Hex to linear color](/images/build-with-thirdweb/hex-to-linear-color.png) + Compile, save, and close `Canvas_HUD`. Run the game. Your car will start red, but after the response from the server, it will turn the color of your last NFT! Drive and collect the NFT collectible, and it will change colors! diff --git a/docs/mini-apps/technical-guides/neynar-notifications.mdx b/docs/mini-apps/technical-guides/neynar-notifications.mdx index c524e2310..09ca618d1 100644 --- a/docs/mini-apps/technical-guides/neynar-notifications.mdx +++ b/docs/mini-apps/technical-guides/neynar-notifications.mdx @@ -33,7 +33,9 @@ Navigate to [dev.neynar.com/app](https://dev.neynar.com/app) and then click on t Copy the url under **Mini app Notifications**. + ![neynar webhook url](/images/miniapps/neynar-notification-webhook.png) + @@ -230,15 +232,21 @@ https://dev.neynar.com/home + ![select app in neynar dev portal](/images/miniapps/neynar-select-app.png) + + ![select app in neynar dev portal](/images/miniapps/neynar-mini-app-tab.png) + + ![select app in neynar dev portal](/images/miniapps/neynar-send-notification.png) + The `target_fids` parameter is the starting point for all filtering. Pass an empty array for `target_fids` to start with the set of all FIDs with notifications enabled for your app, or manually define `target_fids` to list specific FIDs. From af9afba99b2b500cc52e9b1a06772bf05625f98f Mon Sep 17 00:00:00 2001 From: sohey Date: Wed, 21 Jan 2026 22:17:27 +0100 Subject: [PATCH 2/4] added linting script --- .claude/commands/doc-feedback.md | 105 +++++++ .claude/commands/lint.md | 92 ++---- claude.md | 155 ++++++++-- docs/CLAUDE.md | 98 ------- scripts/lint-mdx.js | 469 +++++++++++++++++++++++++++++++ 5 files changed, 725 insertions(+), 194 deletions(-) create mode 100644 .claude/commands/doc-feedback.md delete mode 100644 docs/CLAUDE.md create mode 100755 scripts/lint-mdx.js diff --git a/.claude/commands/doc-feedback.md b/.claude/commands/doc-feedback.md new file mode 100644 index 000000000..68be9e15e --- /dev/null +++ b/.claude/commands/doc-feedback.md @@ -0,0 +1,105 @@ +# Documentation Feedback + +Review changed documentation for quality, consistency, and adherence to the style guide. + +## Instructions + +### 1. Identify files to review + +Get the changed MDX files: + +```bash +git diff --name-only HEAD -- '*.mdx' +git diff --name-only master...HEAD -- '*.mdx' +``` + +If `$ARGUMENTS` is provided, review that specific file or path instead. + +### 2. Run the deterministic linter first + +```bash +node scripts/lint-mdx.js $ARGUMENTS +``` + +If there are errors, note them but continue with the content review. + +### 3. Review against the style guide + +Read `content-instructions.md` for the full style guide. Check each file for: + +**Language and style:** +- Clear, direct language for technical audiences +- Second person ("you") for instructions +- Active voice over passive voice +- Present tense for current states +- Consistent terminology throughout +- Concise sentences with necessary context +- Parallel structure in lists and headings + +**Content organization:** +- Most important information first (inverted pyramid) +- Progressive disclosure: basic before advanced +- Complex procedures broken into numbered steps +- Expected outcomes provided for major steps +- Descriptive, keyword-rich headings +- Logical grouping with clear section breaks + +**User-centered approach:** +- Focus on user goals, not system features +- Common questions anticipated and addressed +- Troubleshooting for likely failure points +- Scannable with headings, lists, white space +- Verification steps to confirm success + +**Code examples:** +- Complete, runnable examples users can copy +- Proper error handling shown +- Realistic data instead of placeholders +- Expected outputs included +- Complex logic has explanatory comments +- No real API keys or secrets + +**Accessibility:** +- Descriptive alt text for images +- Specific link text (not "click here") +- Proper heading hierarchy (start with H2) +- Content structured for scanning + +### 4. Provide feedback + +For each file reviewed, provide: + +``` +## [filename] + +### ✅ What's working well +- [specific positive observations] + +### 🔧 Suggestions +- **[Section/Line]**: [specific issue] → [suggested fix] + +### ⚠️ Linter issues (if any) +- [issues from the linter output] +``` + +### 5. Offer to help + +After providing feedback, ask if the user wants help: +- Fixing specific issues +- Rewriting sections +- Adding missing content + +## Review checklist + +Use this checklist to ensure thorough review: + +- [ ] Terminology is consistent throughout +- [ ] Examples follow standard format (filename/title, language, highlights) +- [ ] All required sections are present (frontmatter, clear structure) +- [ ] Language is clear and direct +- [ ] Instructions use second person and active voice +- [ ] Code examples are complete and runnable +- [ ] No placeholder values like "foo", "bar", "example.com" +- [ ] Headings are descriptive and keyword-rich +- [ ] Content is scannable (headings, lists, white space) +- [ ] Troubleshooting included where appropriate diff --git a/.claude/commands/lint.md b/.claude/commands/lint.md index 86ad2f6a8..54376e33b 100644 --- a/.claude/commands/lint.md +++ b/.claude/commands/lint.md @@ -2,85 +2,27 @@ Check MDX files for formatting, structure, and Mintlify component usage. -Reference `mintlify-reference.md` for correct component syntax. +## Instructions -## Scope - -**By default, only check changed files:** -1. Run `git diff --name-only HEAD` to get uncommitted changes -2. Run `git diff --name-only master...HEAD` to include committed changes on this branch -3. Filter to only `.mdx` files in `docs/` - -**If `$ARGUMENTS` is provided:** -- `all` — check all MDX files in `docs/` -- A specific path — check only that file/folder (e.g., `docs/api`) - -## Steps - -1. Determine which files to check (changed files, all, or specific path) -2. Check each file for the issues below -3. Report issues with file path and line number -4. Suggest fixes for each issue - -## Checks to perform - -### Frontmatter -- Every MDX file should have frontmatter with at least `title` and `description` -- Frontmatter must be valid YAML between `---` fences - -### Heading structure -- No skipped heading levels (e.g., h1 → h3) -- Only one h1 per page (or none if title is in frontmatter) - -### Code blocks -- All code blocks must specify a language (```javascript not just ```) -- Code inside `` blocks should have both language AND label (```javascript Node.js) - -### Mintlify components -Check for correct syntax on these components: - -**Callouts** — must be self-closing or have content: -- ``, ``, ``, ``, `` - -**Structural** — check for proper nesting: -- `` must contain `` children -- `` must contain `` children -- `` must contain `` children - -**Cards** — check required attributes: -- `` should have `title` and `href` -- `` should have `cols` attribute - -**API docs** — check required attributes: -- `` needs `path|body|query|header`, `type`, and ideally `required` or `default` -- `` needs `name` and `type` - -**Media**: -- Images should be wrapped in `` -- `` tags should have `alt` attributes - -### Common mistakes to flag -- Unclosed MDX components (e.g., `` without ``) -- Using HTML comments `` instead of MDX `{/* */}` -- Broken internal links (files that don't exist in docs/) -- Empty code blocks -- Components with typos (e.g., `` instead of ``) - -## Output format +1. Run the deterministic linter script: +```bash +node scripts/lint-mdx.js $ARGUMENTS ``` -## Lint Results -### Files checked -- 3 changed files (use `/lint all` to check entire docs/) +Arguments: +- (none) — check only changed files (git diff) +- `all` — check all MDX files in docs/ +- `docs/path` — check specific file or directory -### ❌ Errors (must fix) -- `docs/getting-started.mdx:15` — Code block missing language specifier -- `docs/api/users.mdx:42` — Unclosed component +2. Review the output and present results to the user -### ⚠️ Warnings (should fix) -- `docs/guides/auth.mdx:8` — Image not wrapped in +3. If errors or warnings are found, offer to help fix them: + - For each error, explain what's wrong and how to fix it + - Prioritize errors over warnings + - Offer to fix issues automatically if the user wants -### ✅ Summary -- 3 files checked, 2 errors, 1 warning -``` +## Reference + +See `mintlify-reference.md` for correct component syntax. +See `content-instructions.md` for writing guidelines. diff --git a/claude.md b/claude.md index 68375ba4c..4f183874a 100644 --- a/claude.md +++ b/claude.md @@ -1,30 +1,143 @@ -# Documentation Repository +# Base Documentation -## Stack -- Mintlify for documentation site -- MDX files in `docs/` folder -- Storybook for component demos +Technical documentation for Base, an Ethereum L2 blockchain. Built with Mintlify. -## Git -- Primary branch: `master` +## Quick Reference + +| Command | Description | +|---------|-------------| +| `mintlify dev` | Local dev server (http://localhost:3000) | +| `mintlify install` | Reinstall dependencies | +| `node scripts/lint-mdx.js` | Lint MDX files (deterministic) | +| `/lint` | Run linter and get help fixing issues | +| `/doc-feedback` | Review docs for quality and style guide adherence | + +## Repository Structure + +``` +docs/ # All documentation content (MDX files) +├── get-started/ # Introduction, quickstarts, builder support +├── base-chain/ # Network info, node operations, tools +├── base-account/ # Smart Wallet, account abstraction +├── base-app/ # Agents, app development +├── mini-apps/ # Mini app development guides +├── onchainkit/ # React component library (versioned) +├── cookbook/ # Use-case tutorials +├── learn/ # Educational content (Solidity, Ethereum) +├── images/ # Assets organized by topic +├── snippets/ # Reusable MDX components +└── docs.json # Navigation and site configuration +storybook/ # Component demos (Chromatic deployment) +``` + +## Documentation Sections + +| Section | Path | Content Type | +|---------|------|--------------| +| Get Started | `get-started/` | Intro, quickstarts, AI prompting | +| Base Chain | `base-chain/` | Network, nodes, tools, security | +| Base Account | `base-account/` | Smart Wallet SDK, integrations | +| Base App | `base-app/` | Agent development | +| Mini Apps | `mini-apps/` | Mini app guides, MiniKit | +| OnchainKit | `onchainkit/` | React components (versioned) | +| Cookbook | `cookbook/` | Practical tutorials | +| Learn | `learn/` | Solidity, Ethereum fundamentals | + +## Content Standards + +### File Format -## Key References -- `mintlify-reference.md` — Mintlify component syntax and usage -- `content-instructions.md` — Content guidelines -- `global-tone-voice.mdx` — Tone and voice standards -- `docs.json` — Site navigation and configuration +Every MDX file requires frontmatter: -## Conventions -- Use American English spelling +```yaml +--- +title: "Clear, keyword-rich title" +description: "Concise value description" +--- +``` + +### Writing Rules + +- American English spelling - Sentence case for headings +- Second person ("you") for instructions +- Active voice, present tense +- No H1 in body (title comes from frontmatter) + +### Code Blocks + +- Always specify language: ` ```typescript ` not ` ``` ` +- Add filename or title: ` ```typescript page.tsx ` or ` ```typescript title="Example" ` +- Blocks >7 lines: add `lines` for line numbers +- Use `highlight={1-2,5}` for emphasis +- Use `wrap` to prevent horizontal scroll + +### Components + +Use sparingly and correctly: + +**Callouts** (for important info only): +- `` - supplementary info +- `` - best practices +- `` - critical cautions +- `` - neutral context +- `` - success confirmation + +**Structure**: +- `` with `` - procedures +- `` with `` - platform-specific content +- `` - same concept in multiple languages +- `` with `` - progressive disclosure + +**Media**: - All images wrapped in `` -- Code blocks must specify a language -- Use Mintlify callouts sparingly (``, ``, ``, ``, ``) +- `` must have `alt` attribute + +**API Docs**: +- `` - parameters +- `` - responses + +### Comments + +Use MDX syntax, not HTML: +```mdx +{/* Correct */} + +``` + +## Navigation + +All navigation is defined in `docs.json`: + +- **Tabs**: Top-level sections (Get Started, Base Chain, etc.) +- **Groups**: Subsections within tabs +- **Pages**: Individual MDX files -## Commands -- `/lint` — Check MDX formatting and Mintlify component usage +When adding pages: +1. Create MDX file in appropriate directory +2. Add path to `docs.json` in correct group + +When removing pages: +1. Delete MDX file +2. Remove from `docs.json` +3. Add redirect in `docs.json` redirects section + +## Git + +- **Primary branch**: `master` +- **Auto-deploy**: Mintlify GitHub App deploys on push to master + +## Key Reference Files + +| File | Purpose | +|------|---------| +| `docs.json` | Site navigation and config | +| `content-instructions.md` | Detailed writing guidelines | +| `mintlify-reference.md` | Component syntax reference | +| `scripts/lint-mdx.js` | Deterministic MDX linter | ## Before Committing -- Run `/lint` and fix any errors -- If removing docs, add redirects in `docs.json` -- Ensure all internal links are valid + +1. Run `/lint` and fix any errors +2. If removing docs, add redirects in `docs.json` +3. Verify internal links work diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md deleted file mode 100644 index f95651952..000000000 --- a/docs/CLAUDE.md +++ /dev/null @@ -1,98 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Repository Overview - -This is the **Base Documentation** repository, a comprehensive documentation site for Base (Ethereum Layer 2 blockchain) built with **Mintlify**. The repository covers blockchain development from basics to advanced topics including smart contracts, onchain apps, and Base ecosystem tools. - -## Key Commands - -### Local Development -```bash -# Install Mintlify CLI globally (required for local development) -npm i -g mintlify - -# Run local development server -mintlify dev - -# Reinstall dependencies if mintlify dev fails -mintlify install -``` - -### Storybook Development -```bash -cd storybook -npm install -npm run storybook # Runs on port 6006 -npm run build-storybook -``` - -## Architecture and Structure - -### Documentation Organization -- **Tab-based navigation** with 7 main sections in `docs.json`: - - Get Started (introduction, quickstarts) - - Base Chain (network info, node operations) - - Smart Wallet (account abstraction) - - OnchainKit (React components) - - Wallet App (MiniKit development) - - Cookbook (use-case guides) - - Learn (educational content) - -### File Structure -- **Main content**: All documentation files are in `/docs` directory as `.mdx` files -- **Configuration**: `docs.json` defines navigation structure and site settings -- **Images**: Organized in `/images` with subdirectories by topic -- **Custom components**: React components available in `/snippets` -- **Styling**: `custom.css` for site-wide styles, `iframe-theme.js` for theme handling - -### Content Types -- **Educational tutorials** (Learn section with Solidity, Ethereum basics) -- **Practical guides** (Cookbook with real-world implementation examples) -- **API documentation** (OnchainKit components and utilities) -- **Tool documentation** (Smart Wallet, development frameworks) - -## Development Workflow -- Use sub-agents to help complete tasks -- Leverage the Github CLI tool to create commits for changes and to read the history of changes -- If you create scratch files for problem solving, remove them when you are done with them. - -### Content Editing -- Edit `.mdx` files directly in `/docs` directory (NOT `/_pages`) -- Use Mintlify syntax for enhanced documentation features -- Changes are automatically deployed via Mintlify GitHub App when pushed to default branch - -### Component Development -- Storybook setup in `/storybook` directory for UI component development -- Components integrated into documentation via iframe embedding -- Chromatic integration for visual testing and regression detection - -### Important Rules -- **Never edit files in `/_pages` directory** - all documentation files are in `/docs` -- Use Mintlify CLI for local development and testing -- Follow existing navigation structure defined in `docs.json` -- Images should be placed in appropriate `/images` subdirectories - -## Special Features - -### AI Integration -- Contextual AI options (ChatGPT, Claude) available in documentation interface -- Large `llms-full.txt` file (11,496 lines) provides comprehensive AI context -- AI prompting guides integrated throughout documentation - -### Interactive Components -- Storybook components embedded via iframes for interactive examples -- Dark/light mode support with custom theme handling -- Custom React components available for enhanced documentation experiences - -### Cross-referencing -- Extensive internal linking between related topics -- Progressive learning paths from beginner to advanced -- Use-case driven organization alongside technical reference material - -## Deployment - -- **Automatic deployment** via Mintlify GitHub App when changes are pushed to default branch -- **Storybook deployment** to Chromatic for component library hosting -- No manual deployment steps required for documentation updates \ No newline at end of file diff --git a/scripts/lint-mdx.js b/scripts/lint-mdx.js new file mode 100755 index 000000000..f28d53570 --- /dev/null +++ b/scripts/lint-mdx.js @@ -0,0 +1,469 @@ +#!/usr/bin/env node + +/** + * MDX Linter for Mintlify Documentation + * + * Deterministic checks for MDX files: + * - Frontmatter validation + * - Heading structure + * - Code block language specifiers + * - Mintlify component syntax + * - Internal link validation + * + * Usage: + * node scripts/lint-mdx.js # Check changed files only + * node scripts/lint-mdx.js all # Check all MDX files + * node scripts/lint-mdx.js docs/api # Check specific path + */ + +const fs = require("fs"); +const path = require("path"); +const { execSync } = require("child_process"); + +const DOCS_DIR = path.join(__dirname, "..", "docs"); + +// ----------------------------------------------------------------------------- +// File Discovery +// ----------------------------------------------------------------------------- + +function getChangedFiles() { + try { + const uncommitted = execSync("git diff --name-only HEAD", { + encoding: "utf-8", + cwd: path.join(__dirname, ".."), + }) + .trim() + .split("\n") + .filter(Boolean); + + const committed = execSync("git diff --name-only master...HEAD", { + encoding: "utf-8", + cwd: path.join(__dirname, ".."), + }) + .trim() + .split("\n") + .filter(Boolean); + + const allChanged = [...new Set([...uncommitted, ...committed])]; + return allChanged.filter( + (f) => f.startsWith("docs/") && f.endsWith(".mdx") + ); + } catch { + return []; + } +} + +function getAllMdxFiles(dir) { + const files = []; + const entries = fs.readdirSync(dir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + files.push(...getAllMdxFiles(fullPath)); + } else if (entry.name.endsWith(".mdx")) { + files.push(path.relative(path.join(__dirname, ".."), fullPath)); + } + } + return files; +} + +function getFilesToCheck(arg) { + if (!arg) { + return { files: getChangedFiles(), mode: "changed" }; + } + if (arg === "all") { + return { files: getAllMdxFiles(DOCS_DIR), mode: "all" }; + } + // Specific path + const targetPath = path.join(__dirname, "..", arg); + if (fs.existsSync(targetPath)) { + if (fs.statSync(targetPath).isDirectory()) { + return { files: getAllMdxFiles(targetPath), mode: `path: ${arg}` }; + } + if (arg.endsWith(".mdx")) { + return { files: [arg], mode: `file: ${arg}` }; + } + } + return { files: [], mode: "invalid path" }; +} + +// ----------------------------------------------------------------------------- +// Linting Rules +// ----------------------------------------------------------------------------- + +function checkFrontmatter(content, filePath) { + const issues = []; + const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); + + if (!frontmatterMatch) { + issues.push({ line: 1, severity: "error", message: "Missing frontmatter" }); + return issues; + } + + const frontmatter = frontmatterMatch[1]; + + if (!/^title:\s*.+/m.test(frontmatter)) { + issues.push({ + line: 1, + severity: "error", + message: "Frontmatter missing required `title` field", + }); + } + + if (!/^description:\s*.+/m.test(frontmatter)) { + issues.push({ + line: 1, + severity: "error", + message: "Frontmatter missing required `description` field", + }); + } + + return issues; +} + +function checkHeadingStructure(content, filePath) { + const issues = []; + const lines = content.split("\n"); + let inCodeBlock = false; + let lastHeadingLevel = 0; + let h1Count = 0; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + if (line.startsWith("```")) { + inCodeBlock = !inCodeBlock; + continue; + } + if (inCodeBlock) continue; + + const headingMatch = line.match(/^(#{1,6})\s+/); + if (headingMatch) { + const level = headingMatch[1].length; + + if (level === 1) { + h1Count++; + if (h1Count > 1) { + issues.push({ + line: i + 1, + severity: "error", + message: "Multiple H1 headings found (should have at most one)", + }); + } + } + + if (lastHeadingLevel > 0 && level > lastHeadingLevel + 1) { + issues.push({ + line: i + 1, + severity: "warning", + message: `Skipped heading level: H${lastHeadingLevel} → H${level}`, + }); + } + + lastHeadingLevel = level; + } + } + + return issues; +} + +function checkCodeBlocks(content, filePath) { + const issues = []; + const lines = content.split("\n"); + let inCodeGroup = false; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + if (line.includes("")) inCodeGroup = true; + if (line.includes("")) inCodeGroup = false; + + // Check for code block opening + const codeBlockMatch = line.match(/^```(\S*)/); + if (codeBlockMatch) { + const lang = codeBlockMatch[1]; + + // Check for empty language + if (!lang) { + issues.push({ + line: i + 1, + severity: "error", + message: "Code block missing language specifier", + }); + } + + // In CodeGroup, should have language AND label + if (inCodeGroup && lang && !lang.includes(" ") && !/\s+\S+/.test(line.slice(3 + lang.length))) { + // Check if there's a label after the language + const afterLang = line.slice(3 + lang.length).trim(); + if (!afterLang) { + issues.push({ + line: i + 1, + severity: "warning", + message: "Code block in should have a label (e.g., ```javascript Node.js)", + }); + } + } + } + } + + return issues; +} + +function checkMintlifyComponents(content, filePath) { + const issues = []; + const lines = content.split("\n"); + + // Track component nesting + const componentStack = []; + + // Components that need specific children + const parentChildRules = { + Steps: "Step", + Tabs: "Tab", + AccordionGroup: "Accordion", + }; + + // Required attributes + const requiredAttrs = { + Step: ["title"], + Tab: ["title"], + Accordion: ["title"], + Card: ["title"], + ParamField: ["type"], + ResponseField: ["name", "type"], + }; + + // Valid callout components + const validCallouts = ["Note", "Tip", "Warning", "Info", "Check"]; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Check for HTML comments + if (line.includes("", + }); + } + + // Check for typos in callouts + const calloutTypos = ["", "", "", "", ""]; + for (const typo of calloutTypos) { + if (line.includes(typo)) { + issues.push({ + line: i + 1, + severity: "error", + message: `Typo: ${typo} should be <${typo.slice(1, -2)}>`, + }); + } + } + + // Check opening tags + const openTagMatch = line.match(/<(Step|Tab|Accordion|Card|CardGroup|ParamField|ResponseField|Frame|Steps|Tabs|AccordionGroup)(\s[^>]*)?\/?>/); + if (openTagMatch) { + const tag = openTagMatch[1]; + const attrs = openTagMatch[2] || ""; + const isSelfClosing = line.includes("/>"); + + // Check required attributes + if (requiredAttrs[tag]) { + for (const attr of requiredAttrs[tag]) { + if (!new RegExp(`${attr}=`).test(attrs)) { + issues.push({ + line: i + 1, + severity: "warning", + message: `<${tag}> should have \`${attr}\` attribute`, + }); + } + } + } + + // CardGroup should have cols + if (tag === "CardGroup" && !attrs.includes("cols")) { + issues.push({ + line: i + 1, + severity: "warning", + message: " should have `cols` attribute", + }); + } + + // Track parent components + if (parentChildRules[tag] && !isSelfClosing) { + componentStack.push({ tag, line: i + 1 }); + } + } + + // Check for img without Frame + if (line.includes("= Math.max(0, i - 5); j--) { + if (lines[j].includes("", + }); + } + } + + // Check for img without alt + if (line.includes(" should have `alt` attribute", + }); + } + } + + return issues; +} + +function checkInternalLinks(content, filePath) { + const issues = []; + const lines = content.split("\n"); + + // Match markdown links and href attributes pointing to internal paths + const linkPatterns = [ + /\[([^\]]*)\]\(\/([^)#]+)/g, // [text](/path) + /href="\/([^"#]+)/g, // href="/path" + ]; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + for (const pattern of linkPatterns) { + let match; + pattern.lastIndex = 0; + + while ((match = pattern.exec(line)) !== null) { + const linkPath = match[pattern === linkPatterns[0] ? 2 : 1]; + + // Skip external-looking paths and anchors + if (linkPath.startsWith("http") || linkPath.startsWith("#")) continue; + + // Skip image paths + if (linkPath.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i)) continue; + + // Check if file exists + const possiblePaths = [ + path.join(DOCS_DIR, linkPath + ".mdx"), + path.join(DOCS_DIR, linkPath, "index.mdx"), + path.join(DOCS_DIR, linkPath), + ]; + + const exists = possiblePaths.some((p) => fs.existsSync(p)); + + if (!exists) { + issues.push({ + line: i + 1, + severity: "warning", + message: `Possibly broken internal link: /${linkPath}`, + }); + } + } + } + } + + return issues; +} + +// ----------------------------------------------------------------------------- +// Main +// ----------------------------------------------------------------------------- + +function lintFile(filePath) { + const fullPath = path.join(__dirname, "..", filePath); + if (!fs.existsSync(fullPath)) { + return [{ line: 0, severity: "error", message: "File not found" }]; + } + + const content = fs.readFileSync(fullPath, "utf-8"); + + const issues = [ + ...checkFrontmatter(content, filePath), + ...checkHeadingStructure(content, filePath), + ...checkCodeBlocks(content, filePath), + ...checkMintlifyComponents(content, filePath), + ...checkInternalLinks(content, filePath), + ]; + + return issues.sort((a, b) => a.line - b.line); +} + +function main() { + const arg = process.argv[2]; + const { files, mode } = getFilesToCheck(arg); + + console.log("## Lint Results\n"); + console.log(`### Files checked`); + console.log(`- ${files.length} files (${mode})`); + + if (files.length === 0) { + if (mode === "changed") { + console.log("- No changed MDX files found\n"); + } else { + console.log("- No files to check\n"); + } + console.log("### ✅ Summary"); + console.log("- 0 files checked, 0 errors, 0 warnings"); + process.exit(0); + } + + console.log(""); + + const allErrors = []; + const allWarnings = []; + + for (const file of files) { + const issues = lintFile(file); + for (const issue of issues) { + const entry = `\`${file}:${issue.line}\` — ${issue.message}`; + if (issue.severity === "error") { + allErrors.push(entry); + } else { + allWarnings.push(entry); + } + } + } + + if (allErrors.length > 0) { + console.log("### ❌ Errors (must fix)"); + for (const e of allErrors) { + console.log(`- ${e}`); + } + console.log(""); + } + + if (allWarnings.length > 0) { + console.log("### ⚠️ Warnings (should fix)"); + for (const w of allWarnings) { + console.log(`- ${w}`); + } + console.log(""); + } + + if (allErrors.length === 0 && allWarnings.length === 0) { + console.log("### ✅ All checks passed\n"); + } + + console.log("### Summary"); + console.log( + `- ${files.length} files checked, ${allErrors.length} errors, ${allWarnings.length} warnings` + ); + + // Exit with error code if there are errors + process.exit(allErrors.length > 0 ? 1 : 0); +} + +main(); From a57894fb8876f806dcfc4191531d9c35f0b07a6b Mon Sep 17 00:00:00 2001 From: sohey Date: Wed, 21 Jan 2026 22:20:11 +0100 Subject: [PATCH 3/4] updated linting script --- scripts/lint-mdx.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/lint-mdx.js b/scripts/lint-mdx.js index f28d53570..07c42575b 100755 --- a/scripts/lint-mdx.js +++ b/scripts/lint-mdx.js @@ -128,6 +128,7 @@ function checkHeadingStructure(content, filePath) { let inCodeBlock = false; let lastHeadingLevel = 0; let h1Count = 0; + let totalHeadingCount = 0; for (let i = 0; i < lines.length; i++) { const line = lines[i]; @@ -141,6 +142,7 @@ function checkHeadingStructure(content, filePath) { const headingMatch = line.match(/^(#{1,6})\s+/); if (headingMatch) { const level = headingMatch[1].length; + totalHeadingCount++; if (level === 1) { h1Count++; @@ -165,6 +167,15 @@ function checkHeadingStructure(content, filePath) { } } + // Check for pages with no headings (bad for SEO) + if (totalHeadingCount === 0) { + issues.push({ + line: 1, + severity: "warning", + message: "No headings found (at least one heading improves SEO)", + }); + } + return issues; } From a9c82a8e29dbaff51c227c54ba16f04934af7de2 Mon Sep 17 00:00:00 2001 From: sohey Date: Wed, 21 Jan 2026 22:38:38 +0100 Subject: [PATCH 4/4] updated linting script --- .claude/commands/doc-feedback.md | 105 ---------------------- .claude/commands/lint.md | 28 ------ .claude/skills/doc-feedback.md | 42 +++++++++ .claude/skills/lint.md | 25 ++++++ claude.md | 147 +++++++------------------------ scripts/README.md | 48 ++++++++++ 6 files changed, 149 insertions(+), 246 deletions(-) delete mode 100644 .claude/commands/doc-feedback.md delete mode 100644 .claude/commands/lint.md create mode 100644 .claude/skills/doc-feedback.md create mode 100644 .claude/skills/lint.md create mode 100644 scripts/README.md diff --git a/.claude/commands/doc-feedback.md b/.claude/commands/doc-feedback.md deleted file mode 100644 index 68be9e15e..000000000 --- a/.claude/commands/doc-feedback.md +++ /dev/null @@ -1,105 +0,0 @@ -# Documentation Feedback - -Review changed documentation for quality, consistency, and adherence to the style guide. - -## Instructions - -### 1. Identify files to review - -Get the changed MDX files: - -```bash -git diff --name-only HEAD -- '*.mdx' -git diff --name-only master...HEAD -- '*.mdx' -``` - -If `$ARGUMENTS` is provided, review that specific file or path instead. - -### 2. Run the deterministic linter first - -```bash -node scripts/lint-mdx.js $ARGUMENTS -``` - -If there are errors, note them but continue with the content review. - -### 3. Review against the style guide - -Read `content-instructions.md` for the full style guide. Check each file for: - -**Language and style:** -- Clear, direct language for technical audiences -- Second person ("you") for instructions -- Active voice over passive voice -- Present tense for current states -- Consistent terminology throughout -- Concise sentences with necessary context -- Parallel structure in lists and headings - -**Content organization:** -- Most important information first (inverted pyramid) -- Progressive disclosure: basic before advanced -- Complex procedures broken into numbered steps -- Expected outcomes provided for major steps -- Descriptive, keyword-rich headings -- Logical grouping with clear section breaks - -**User-centered approach:** -- Focus on user goals, not system features -- Common questions anticipated and addressed -- Troubleshooting for likely failure points -- Scannable with headings, lists, white space -- Verification steps to confirm success - -**Code examples:** -- Complete, runnable examples users can copy -- Proper error handling shown -- Realistic data instead of placeholders -- Expected outputs included -- Complex logic has explanatory comments -- No real API keys or secrets - -**Accessibility:** -- Descriptive alt text for images -- Specific link text (not "click here") -- Proper heading hierarchy (start with H2) -- Content structured for scanning - -### 4. Provide feedback - -For each file reviewed, provide: - -``` -## [filename] - -### ✅ What's working well -- [specific positive observations] - -### 🔧 Suggestions -- **[Section/Line]**: [specific issue] → [suggested fix] - -### ⚠️ Linter issues (if any) -- [issues from the linter output] -``` - -### 5. Offer to help - -After providing feedback, ask if the user wants help: -- Fixing specific issues -- Rewriting sections -- Adding missing content - -## Review checklist - -Use this checklist to ensure thorough review: - -- [ ] Terminology is consistent throughout -- [ ] Examples follow standard format (filename/title, language, highlights) -- [ ] All required sections are present (frontmatter, clear structure) -- [ ] Language is clear and direct -- [ ] Instructions use second person and active voice -- [ ] Code examples are complete and runnable -- [ ] No placeholder values like "foo", "bar", "example.com" -- [ ] Headings are descriptive and keyword-rich -- [ ] Content is scannable (headings, lists, white space) -- [ ] Troubleshooting included where appropriate diff --git a/.claude/commands/lint.md b/.claude/commands/lint.md deleted file mode 100644 index 54376e33b..000000000 --- a/.claude/commands/lint.md +++ /dev/null @@ -1,28 +0,0 @@ -# Lint MDX Documentation - -Check MDX files for formatting, structure, and Mintlify component usage. - -## Instructions - -1. Run the deterministic linter script: - -```bash -node scripts/lint-mdx.js $ARGUMENTS -``` - -Arguments: -- (none) — check only changed files (git diff) -- `all` — check all MDX files in docs/ -- `docs/path` — check specific file or directory - -2. Review the output and present results to the user - -3. If errors or warnings are found, offer to help fix them: - - For each error, explain what's wrong and how to fix it - - Prioritize errors over warnings - - Offer to fix issues automatically if the user wants - -## Reference - -See `mintlify-reference.md` for correct component syntax. -See `content-instructions.md` for writing guidelines. diff --git a/.claude/skills/doc-feedback.md b/.claude/skills/doc-feedback.md new file mode 100644 index 000000000..17d3de3d4 --- /dev/null +++ b/.claude/skills/doc-feedback.md @@ -0,0 +1,42 @@ +--- +name: reviewing-documentation +description: Reviews documentation for quality, consistency, and style guide adherence. Use when reviewing changed docs or before publishing new content. +--- + +# Documentation Feedback + +## Workflow + +1. **Get files to review** + ```bash + git diff --name-only HEAD -- '*.mdx' + git diff --name-only master...HEAD -- '*.mdx' + ``` + If `$ARGUMENTS` provided, review that path instead. + +2. **Run linter first** + ```bash + node scripts/lint-mdx.js $ARGUMENTS + ``` + +3. **Review against style guide** — See [content-instructions.md](../../content-instructions.md) + +4. **Provide feedback** per file: + - What's working well + - Specific suggestions with line references + - Linter issues (if any) + +5. **Offer to fix** issues if requested + +## Review checklist + +``` +Review Progress: +- [ ] Terminology consistent +- [ ] Code examples complete and runnable +- [ ] No placeholder values (foo, bar, example.com) +- [ ] Headings descriptive and keyword-rich +- [ ] Content scannable (headings, lists, white space) +- [ ] Active voice, second person +- [ ] Troubleshooting included where appropriate +``` diff --git a/.claude/skills/lint.md b/.claude/skills/lint.md new file mode 100644 index 000000000..5a2076e21 --- /dev/null +++ b/.claude/skills/lint.md @@ -0,0 +1,25 @@ +--- +name: linting-mdx +description: Runs deterministic MDX linter and helps fix formatting, structure, and Mintlify component issues. Use when checking documentation quality or before committing changes. +--- + +# Lint MDX + +Run the linter: + +```bash +node scripts/lint-mdx.js $ARGUMENTS +``` + +Arguments: (none) = changed files, `all` = everything, or specify a path. + +## Workflow + +1. Run linter, present results +2. If errors found, offer to fix them +3. Prioritize errors over warnings + +## References + +- [mintlify-reference.md](../../mintlify-reference.md) — component syntax +- [content-instructions.md](../../content-instructions.md) — writing guidelines diff --git a/claude.md b/claude.md index 4f183874a..1b459c5c9 100644 --- a/claude.md +++ b/claude.md @@ -1,143 +1,64 @@ # Base Documentation -Technical documentation for Base, an Ethereum L2 blockchain. Built with Mintlify. +Technical documentation for Base (Ethereum L2). Built with Mintlify. -## Quick Reference +## Commands | Command | Description | |---------|-------------| -| `mintlify dev` | Local dev server (http://localhost:3000) | -| `mintlify install` | Reinstall dependencies | -| `node scripts/lint-mdx.js` | Lint MDX files (deterministic) | -| `/lint` | Run linter and get help fixing issues | -| `/doc-feedback` | Review docs for quality and style guide adherence | +| `mintlify dev` | Local dev server | +| `/lint` | Lint MDX files and fix issues | +| `/doc-feedback` | Review content quality | -## Repository Structure +## Structure ``` -docs/ # All documentation content (MDX files) -├── get-started/ # Introduction, quickstarts, builder support -├── base-chain/ # Network info, node operations, tools -├── base-account/ # Smart Wallet, account abstraction -├── base-app/ # Agents, app development -├── mini-apps/ # Mini app development guides -├── onchainkit/ # React component library (versioned) -├── cookbook/ # Use-case tutorials -├── learn/ # Educational content (Solidity, Ethereum) -├── images/ # Assets organized by topic -├── snippets/ # Reusable MDX components -└── docs.json # Navigation and site configuration -storybook/ # Component demos (Chromatic deployment) +docs/ +├── get-started/ # Intro, quickstarts +├── base-chain/ # Network, nodes, tools +├── base-account/ # Smart Wallet SDK +├── base-app/ # Agent development +├── mini-apps/ # MiniKit guides +├── onchainkit/ # React components (versioned) +├── cookbook/ # Tutorials +├── learn/ # Solidity, Ethereum basics +├── images/ # Assets by topic +├── snippets/ # Reusable MDX components +└── docs.json # Navigation config ``` -## Documentation Sections - -| Section | Path | Content Type | -|---------|------|--------------| -| Get Started | `get-started/` | Intro, quickstarts, AI prompting | -| Base Chain | `base-chain/` | Network, nodes, tools, security | -| Base Account | `base-account/` | Smart Wallet SDK, integrations | -| Base App | `base-app/` | Agent development | -| Mini Apps | `mini-apps/` | Mini app guides, MiniKit | -| OnchainKit | `onchainkit/` | React components (versioned) | -| Cookbook | `cookbook/` | Practical tutorials | -| Learn | `learn/` | Solidity, Ethereum fundamentals | - -## Content Standards - -### File Format - -Every MDX file requires frontmatter: +## Content Rules +**Frontmatter** (required): ```yaml --- -title: "Clear, keyword-rich title" -description: "Concise value description" +title: "Keyword-rich title" +description: "Value description" --- ``` -### Writing Rules - -- American English spelling -- Sentence case for headings -- Second person ("you") for instructions -- Active voice, present tense -- No H1 in body (title comes from frontmatter) - -### Code Blocks - -- Always specify language: ` ```typescript ` not ` ``` ` -- Add filename or title: ` ```typescript page.tsx ` or ` ```typescript title="Example" ` -- Blocks >7 lines: add `lines` for line numbers -- Use `highlight={1-2,5}` for emphasis -- Use `wrap` to prevent horizontal scroll - -### Components - -Use sparingly and correctly: +**Writing**: American English, sentence case headings, second person ("you"), active voice. -**Callouts** (for important info only): -- `` - supplementary info -- `` - best practices -- `` - critical cautions -- `` - neutral context -- `` - success confirmation +**Code blocks**: Always specify language. Add filename or title. Use `highlight={}` for emphasis. -**Structure**: -- `` with `` - procedures -- `` with `` - platform-specific content -- `` - same concept in multiple languages -- `` with `` - progressive disclosure +**Components**: See [mintlify-reference.md](mintlify-reference.md) for syntax. -**Media**: -- All images wrapped in `` -- `` must have `alt` attribute - -**API Docs**: -- `` - parameters -- `` - responses - -### Comments - -Use MDX syntax, not HTML: -```mdx -{/* Correct */} - -``` +**Images**: Wrap in ``, include `alt` attribute. ## Navigation -All navigation is defined in `docs.json`: - -- **Tabs**: Top-level sections (Get Started, Base Chain, etc.) -- **Groups**: Subsections within tabs -- **Pages**: Individual MDX files - -When adding pages: -1. Create MDX file in appropriate directory -2. Add path to `docs.json` in correct group - -When removing pages: -1. Delete MDX file -2. Remove from `docs.json` -3. Add redirect in `docs.json` redirects section - -## Git - -- **Primary branch**: `master` -- **Auto-deploy**: Mintlify GitHub App deploys on push to master +Edit `docs.json` to add/remove pages. Add redirects when removing pages. -## Key Reference Files +## References | File | Purpose | |------|---------| -| `docs.json` | Site navigation and config | -| `content-instructions.md` | Detailed writing guidelines | -| `mintlify-reference.md` | Component syntax reference | -| `scripts/lint-mdx.js` | Deterministic MDX linter | +| [content-instructions.md](content-instructions.md) | Writing guidelines | +| [mintlify-reference.md](mintlify-reference.md) | Component syntax | +| [scripts/README.md](scripts/README.md) | Linter usage | ## Before Committing -1. Run `/lint` and fix any errors -2. If removing docs, add redirects in `docs.json` -3. Verify internal links work +1. Run `/lint` and fix errors +2. Add redirects for removed pages +3. Verify links work diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 000000000..18a441a27 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,48 @@ +# Scripts + +## MDX Linter + +Deterministic linter for MDX documentation files. + +### Usage + +```bash +# Check only files you've changed (default) +node scripts/lint-mdx.js + +# Check a specific file +node scripts/lint-mdx.js docs/cookbook/my-guide.mdx + +# Check a directory +node scripts/lint-mdx.js docs/onchainkit + +# Check all MDX files +node scripts/lint-mdx.js all +``` + +### Checks + +| Check | Severity | Description | +|-------|----------|-------------| +| Frontmatter | Error | `title` and `description` required | +| Headings | Error | Max one H1, no skipped levels | +| Headings | Warning | At least one heading per page (SEO) | +| Code blocks | Error | Language specifier required | +| Code blocks | Warning | Labels required in `` | +| Components | Warning | Required attributes on Mintlify components | +| Comments | Error | MDX `{/* */}` not HTML `` | +| Links | Warning | Internal links must point to existing files | + +### Exit codes + +| Code | Meaning | +|------|---------| +| `0` | No errors (warnings may exist) | +| `1` | Errors found | + +### CI Integration + +```bash +# Fail CI if linting errors exist +node scripts/lint-mdx.js all || exit 1 +```