diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index b6d1da49d..d950633bf 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -7,6 +7,9 @@
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {},
+ "ghcr.io/devcontainers/features/node:1": {
+ "version": "22"
+ },
"ghcr.io/devcontainers/features/python:1": {}
},
diff --git a/.devcontainer/postCreateCommands.sh b/.devcontainer/postCreateCommands.sh
index bc62f4925..2d43cfea2 100755
--- a/.devcontainer/postCreateCommands.sh
+++ b/.devcontainer/postCreateCommands.sh
@@ -1,4 +1,6 @@
-#/!bin/sh
+#!/bin/sh
+
+set -e
# Command to run during postCreateCommand
# Done in script because when done via object the commands are run in parallel
@@ -7,4 +9,5 @@
# https://containers.dev/implementors/json_reference/#formatting-string-vs-array-properties
pip install -r requirements.txt
+make sdk-install
make html
diff --git a/.gitignore b/.gitignore
index 4ccc56e36..564bb7782 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,5 @@ _build
**/.DS_Store
.local/
.vscode
+node_modules/
+_static/vendor/evo-sdk.js
diff --git a/.readthedocs.yml b/.readthedocs.yml
index a429d8270..6c283608a 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -12,6 +12,11 @@ build:
os: "ubuntu-22.04"
tools:
python: "3.10"
+ nodejs: "22"
+ jobs:
+ pre_build:
+ - npm ci
+ - npm run build:sdk
python:
install:
diff --git a/CLAUDE.md b/CLAUDE.md
index 9f614f5ce..6d4be0a09 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -14,7 +14,12 @@ python -m venv venv
source venv/bin/activate # or venv\Scripts\activate on Windows
pip install -r requirements.txt
-# Build documentation
+# Install JavaScript build dependencies (once, on a fresh checkout; needed to
+# bundle the Evo SDK for the interactive tutorial runners)
+make sdk-install
+
+# Build documentation (also regenerates the gitignored SDK bundle via the
+# `sdk` target)
make html
# Clean build
@@ -30,7 +35,12 @@ python3 scripts/tutorial-sync/sync_tutorial_code.py --source /path/to/platform-t
python3 scripts/tutorial-sync/sync_tutorial_code.py --check --source /path/to/platform-tutorials
# View built documentation
-# Open _build/html/index.html in browser
+# Most pages can be opened directly in a browser
+# Open _build/html/index.html
+
+# To use interactive tutorial widgets, serve the build over HTTP because
+# browsers block their SDK module import from file:// pages
+python -m http.server 8000 -d _build/html
```
## Architecture
@@ -64,6 +74,7 @@ The site uses a hierarchical structure with:
- GitHub integration for edit links and source references
- Google Analytics tracking configured
- Uses pydata-sphinx-theme with custom CSS overrides
+- Interactive tutorial runners (`_static/js/interactive-tutorial.js`) import the Evo SDK from `_static/vendor/evo-sdk.js`, a gitignored bundle produced by `npm run build:sdk` (esbuild, pinned via `package-lock.json`). Read the Docs builds it in a `pre_build` job; locally `make html` rebuilds it automatically (run `make sdk-install` once first). To change the SDK version, update `package.json`/`package-lock.json` and rebuild.
## Editing Guidelines
@@ -100,4 +111,4 @@ For the full per-release endpoint review process (proto diff, example refresh, d
- Configuration: `conf.py`, `requirements.txt`
- Templates: `_templates/*.html`
- Assets: `_static/**/*`
-- Build output: `_build/html/` (excluded from git)
\ No newline at end of file
+- Build output: `_build/html/` (excluded from git)
diff --git a/Makefile b/Makefile
index d4bb2cbb9..dc089887f 100644
--- a/Makefile
+++ b/Makefile
@@ -7,12 +7,33 @@ SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
+NPM ?= npm
+SDK_BUNDLER = node_modules/.bin/esbuild
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
-.PHONY: help Makefile
+.PHONY: help sdk-install sdk html Makefile
+
+# Install the pinned JavaScript build dependencies for local development.
+# Read the Docs performs this step separately in .readthedocs.yml.
+sdk-install:
+ $(NPM) ci
+
+# Generate the browser SDK asset copied by Sphinx with the other static files.
+sdk: $(SDK_BUNDLER)
+ $(NPM) run build:sdk
+
+# Give a useful setup hint instead of letting npm fail with "esbuild: not found".
+$(SDK_BUNDLER):
+ @echo "Missing JavaScript build dependencies."
+ @echo "Run 'make sdk-install' once, then retry 'make html'."
+ @false
+
+# Keep the generated SDK bundle current for local HTML builds.
+html: sdk
+ @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
diff --git a/_static/css/pydata-overrides.css b/_static/css/pydata-overrides.css
index 2600e17fd..619bf8922 100644
--- a/_static/css/pydata-overrides.css
+++ b/_static/css/pydata-overrides.css
@@ -192,3 +192,243 @@ sphinx search extension interface.
max-height: 60rem;
overflow: auto;
}
+/* Reusable, browser-executed blocks for safe tutorial operations. */
+.interactive-tutorial {
+ margin: 1rem 0 1.5rem;
+ padding: 1rem;
+ border: 1px solid var(--pst-color-border);
+ border-radius: 0.5rem;
+ background: var(--pst-color-surface);
+}
+
+.interactive-tutorial__header,
+.interactive-tutorial__controls,
+.interactive-tutorial__summary {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+}
+
+.interactive-tutorial__header {
+ justify-content: space-between;
+ margin-bottom: 1rem;
+}
+
+/* Integrated runner layout used directly beneath a tutorial code example. */
+.interactive-tutorial--integrated {
+ margin-top: -0.5rem;
+ border-top-left-radius: 0.25rem;
+ border-top-right-radius: 0.25rem;
+}
+
+.interactive-tutorial__toolbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ margin: -1rem -1rem 1rem;
+ padding: 0.65rem 1rem;
+ border-bottom: 1px solid var(--pst-color-border);
+ font-size: 0.9rem;
+}
+
+.interactive-tutorial__help,
+.interactive-tutorial__connection,
+.interactive-tutorial__empty {
+ color: var(--pst-color-text-muted);
+ font-size: 0.85rem;
+}
+
+.interactive-tutorial__connection[data-state="connected"] {
+ color: #16834a;
+}
+
+.interactive-tutorial__connection[data-state="error"] {
+ color: var(--pst-color-danger);
+}
+
+.interactive-tutorial__connection:empty {
+ display: none;
+}
+
+.interactive-tutorial__label {
+ display: block;
+ margin-bottom: 0.35rem;
+ font-weight: 600;
+}
+
+.interactive-tutorial__field {
+ flex: 1 1 12rem;
+ min-width: 0;
+}
+
+.interactive-tutorial__field--wide {
+ flex-basis: 28rem;
+}
+
+.interactive-tutorial__field--small {
+ flex: 0 1 6rem;
+}
+
+.interactive-tutorial__field .interactive-tutorial__input {
+ width: 100%;
+}
+
+.interactive-tutorial__actions {
+ display: flex;
+ gap: 0.75rem;
+ margin-top: 0.75rem;
+}
+
+.interactive-tutorial__controls--secondary {
+ margin-top: 0.75rem;
+}
+
+.interactive-tutorial__input {
+ flex: 1 1 28rem;
+ min-width: 0;
+ padding: 0.55rem 0.7rem;
+ border: 1px solid var(--pst-color-border);
+ border-radius: 0.3rem;
+ background: var(--pst-color-background);
+ color: var(--pst-color-text-base);
+ font-family: var(--pst-font-family-monospace);
+}
+
+.interactive-tutorial__button {
+ padding: 0.4rem 0.8rem;
+ border: 1px solid var(--pst-color-primary);
+ border-radius: 0.3rem;
+ background: var(--pst-color-primary);
+ color: #fff;
+ cursor: pointer;
+ white-space: nowrap;
+}
+
+.interactive-tutorial__button--secondary {
+ background: transparent;
+ color: var(--pst-color-primary);
+}
+
+.interactive-tutorial__button--text {
+ padding-right: 0.25rem;
+ padding-left: 0.25rem;
+ border-color: transparent;
+ background: transparent;
+ color: var(--pst-color-primary);
+}
+
+.interactive-tutorial__button--text:hover {
+ text-decoration: underline;
+}
+
+.interactive-tutorial__button:disabled {
+ cursor: wait;
+ opacity: 0.65;
+}
+
+.interactive-tutorial__help {
+ margin-top: 0.45rem;
+}
+
+.interactive-tutorial__source {
+ margin-top: 1rem;
+}
+
+.interactive-tutorial__source summary {
+ cursor: pointer;
+ font-weight: 600;
+}
+
+.interactive-tutorial__source pre {
+ margin: 0.5rem 0 0;
+ padding: 0.85rem;
+ border: 1px solid var(--pst-color-border);
+ border-radius: 0.3rem;
+ background: var(--pst-color-on-background);
+ overflow: auto;
+}
+
+.interactive-tutorial__result {
+ margin-top: 1rem;
+ padding: 0.85rem;
+ border-radius: 0.3rem;
+ background: var(--pst-color-on-background);
+}
+
+.interactive-tutorial--integrated .interactive-tutorial__result {
+ padding-top: 0.6rem;
+ padding-bottom: 0.6rem;
+}
+
+.interactive-tutorial--integrated .interactive-tutorial__summary {
+ margin-bottom: 0.35rem;
+}
+
+.interactive-tutorial__summary {
+ flex-wrap: wrap;
+ margin-bottom: 0.75rem;
+}
+
+.interactive-tutorial__metric {
+ min-width: 0;
+ max-width: 100%;
+ padding-right: 1rem;
+}
+
+.interactive-tutorial__metric strong,
+.interactive-tutorial__metric span {
+ display: block;
+}
+
+.interactive-tutorial__metric strong {
+ overflow-wrap: anywhere;
+}
+
+.interactive-tutorial__metric span {
+ color: var(--pst-color-text-muted);
+ font-size: 0.75rem;
+}
+
+.interactive-tutorial__json {
+ max-height: 28rem;
+ margin: 0;
+ overflow: auto;
+ white-space: pre;
+ font-size: 0.82rem;
+}
+
+.interactive-tutorial__error {
+ color: var(--pst-color-danger);
+}
+
+@media (max-width: 767.98px) {
+ .interactive-tutorial__controls,
+ .interactive-tutorial__actions {
+ align-items: stretch;
+ flex-direction: column;
+ }
+
+ .interactive-tutorial__input,
+ .interactive-tutorial__field,
+ .interactive-tutorial__field--wide,
+ .interactive-tutorial__field--small {
+ flex: none;
+ width: 100%;
+ }
+
+ .interactive-tutorial__summary {
+ align-items: stretch;
+ flex-direction: column;
+ }
+
+ .interactive-tutorial__metric {
+ padding-right: 0;
+ }
+
+ .interactive-tutorial__toolbar {
+ align-items: flex-start;
+ flex-direction: column;
+ gap: 0.2rem;
+ }
+}
diff --git a/_static/dashmint-lite.html b/_static/dashmint-lite.html
index 841166c65..c3be817cf 100644
--- a/_static/dashmint-lite.html
+++ b/_static/dashmint-lite.html
@@ -119,7 +119,7 @@
Browse cards
// package and serves it as a browser-native ES module. Pinned to the same
// version the React app at ../package.json depends on so both UIs behave
// identically against the same testnet contract.
- import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.0';
+ import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.1';
// The token-enabled "card" data contract is already published on testnet by
// the React app. Anyone querying with the same contract id hits the same
diff --git a/_static/dashnote-lite.html b/_static/dashnote-lite.html
index e1241e054..0c4438060 100644
--- a/_static/dashnote-lite.html
+++ b/_static/dashnote-lite.html
@@ -129,7 +129,7 @@
Get note by ID
// package and serves it as a browser-native ES module. Pinned to the same
// version the React app at ../package.json depends on so both UIs behave
// identically against the same testnet contract.
- import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.0';
+ import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.1';
// The "note" data contract is already published on testnet by the React app.
// Anyone querying with the same contract id hits the same documents.
diff --git a/_static/dashproof-lite.html b/_static/dashproof-lite.html
index 8898daed1..583e2f3cd 100644
--- a/_static/dashproof-lite.html
+++ b/_static/dashproof-lite.html
@@ -120,7 +120,7 @@
+```
+
Once this returns successfully, you're ready to begin developing! See the [Quickstart](../tutorials/introduction.md#quickstart) for recommended next steps. For details on SDK methods, please refer to the [SDK documentation](https://evo-sdk.dash.org/docs.html).
## Connect to a Local Devnet
diff --git a/docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md b/docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md
index 422b826f6..113905a6c 100644
--- a/docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md
+++ b/docs/tutorials/contracts-and-documents/retrieve-a-data-contract.md
@@ -34,6 +34,31 @@ try {
}
```
+```{raw} html
+
+
+ Run this example on testnet
+
+
+
+
+
+
+
+
+
+ View browser code
+
+
+
+
Run the query to inspect the contract.
+
+
+```
+
## Example Data Contract
The following example response shows a retrieved contract:
diff --git a/docs/tutorials/contracts-and-documents/retrieve-data-contract-history.md b/docs/tutorials/contracts-and-documents/retrieve-data-contract-history.md
index c5918c5c2..a36f438e4 100644
--- a/docs/tutorials/contracts-and-documents/retrieve-data-contract-history.md
+++ b/docs/tutorials/contracts-and-documents/retrieve-data-contract-history.md
@@ -49,6 +49,31 @@ try {
}
```
+```{raw} html
+
+
+ Run this example on testnet
+
+
+
+
+
+
+
+
+
+ View browser code
+
+
+
+
Run the query to inspect contract revisions.
+
+
+```
+
## Example data contract history
The following example response shows a retrieved contract history:
diff --git a/docs/tutorials/contracts-and-documents/retrieve-documents.md b/docs/tutorials/contracts-and-documents/retrieve-documents.md
index 21121f09e..f1bb8d936 100644
--- a/docs/tutorials/contracts-and-documents/retrieve-documents.md
+++ b/docs/tutorials/contracts-and-documents/retrieve-documents.md
@@ -41,6 +41,47 @@ try {
}
```
+```{raw} html
+
+
+ Run this example on testnet
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ View browser code
+
+
+
+
Run the query to inspect matching documents.
+
+
+```
+
### Queries
The example code uses a very basic query to return only two results. More extensive querying capabilities are covered in the [query syntax reference](../../reference/query-syntax.md).
diff --git a/docs/tutorials/identities-and-names/retrieve-a-name.md b/docs/tutorials/identities-and-names/retrieve-a-name.md
index 70586df1e..8a71f024f 100644
--- a/docs/tutorials/identities-and-names/retrieve-a-name.md
+++ b/docs/tutorials/identities-and-names/retrieve-a-name.md
@@ -34,6 +34,31 @@ try {
}
```
+```{raw} html
+
+```
+
## Example Identity
The following example response shows a retrieved identity:
diff --git a/docs/tutorials/tokens/retrieve-token-info.md b/docs/tutorials/tokens/retrieve-token-info.md
index 38b3af04e..34d95b59c 100644
--- a/docs/tutorials/tokens/retrieve-token-info.md
+++ b/docs/tutorials/tokens/retrieve-token-info.md
@@ -72,6 +72,57 @@ try {
}
```
+```{raw} html
+
+
+ Run this example on testnet
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ View browser code
+
+
+
+
Run the query to inspect token information and balances.