Compare commits

..

2 Commits

Author SHA1 Message Date
Maria Shodunke
98552291e9 Landing page v2 2026-02-23 12:19:50 +00:00
Maria Shodunke
8c6955b72c Update Tutorials landing page 2026-02-23 10:42:31 +00:00
11 changed files with 570 additions and 36 deletions

View File

@@ -577,6 +577,16 @@ Stay ahead of the curve with the latest developments in RWA tokenization on the
Join the Developer Discord: 開発者ディスコードに参加
Sign up for the Newsletter: ニュースレターに登録
# docs/tutorials/index.page.tsx
Crypto Wallet and Blockchain Development Tutorials: 暗号資産ウォレットやブロックチェーン開発のチュートリアル
These tutorials walk you through the basics of building a very simple XRP Ledger-connected application using your favorite programming language.: これらのチュートリアルでは、お好きなプログラミング言語を使って、XRP Ledgerに接続するアプリケーションを構築するための基礎について説明します。
Get Started with SDKs: SDKを使って始める
Using the xrpl.js client library.: クライアントライブラリxrpl.jsを使用。
Using xrpl.py, a pure Python library.: Pythonライブラリxrpl.pyを使用。
Using xrpl4j, a pure Java library.: Javaライブラリを使用。
Using the XRPL_PHP client library.: PHPライブラリXRPL_PHPを使用。
Access the XRP Ledger directly through the APIs of its core server.: コアサーバのAPIを通じてXRP Ledgerに直接アクセス。
# community/index.page.tsx
XRPL Community: XRPLコミュニティ
community.index.h1part1: 開発者とイノベーターによる

View File

@@ -1,13 +1,13 @@
import { indexPages } from './plugins/index-pages.js';
import { codeSamples } from './plugins/code-samples.js';
import { blogPosts } from './plugins/blog-posts.js';
import { tutorialLanguages } from './plugins/tutorial-languages.js';
export default function customPlugin() {
const indexPagesInst = indexPages();
const codeSamplesInst = codeSamples();
const blogPostsInst = blogPosts();
const tutorialLanguagesInst = tutorialLanguages();
/** @type {import("@redocly/realm/dist/server/plugins/types").PluginInstance } */
const pluginInstance = {
@@ -16,6 +16,7 @@ export default function customPlugin() {
await indexPagesInst.processContent?.(content, actions);
await codeSamplesInst.processContent?.(content, actions);
await blogPostsInst.processContent?.(content, actions);
await tutorialLanguagesInst.processContent?.(content, actions);
},
afterRoutesCreated: async (content, actions) => {
await indexPagesInst.afterRoutesCreated?.(content, actions);

View File

@@ -0,0 +1,118 @@
// @ts-check
/**
* Plugin to detect languages supported in tutorial pages by scanning for tab labels.
* This creates shared data that maps tutorial paths to their supported languages.
*/
export function tutorialLanguages() {
/** @type {import("@redocly/realm/dist/server/plugins/types").PluginInstance } */
const instance = {
processContent: async (actions, { fs, cache }) => {
try {
/** @type {Record<string, string[]>} */
const tutorialLanguagesMap = {};
const allFiles = await fs.scan();
// Find all markdown files in tutorials directory
const tutorialFiles = allFiles.filter((file) =>
file.relativePath.match(/^docs[\/\\]tutorials[\/\\].*\.md$/)
);
for (const { relativePath } of tutorialFiles) {
try {
const { data } = await cache.load(relativePath, 'markdown-ast');
const languages = extractLanguagesFromAst(data.ast);
if (languages.length > 0) {
// Convert file path to URL path
const urlPath = '/' + relativePath
.replace(/\.md$/, '/')
.replace(/\\/g, '/');
tutorialLanguagesMap[urlPath] = languages;
}
} catch (err) {
continue; // Skip files that can't be parsed.
}
}
actions.createSharedData('tutorial-languages', tutorialLanguagesMap);
actions.addRouteSharedData('/docs/tutorials/', 'tutorial-languages', 'tutorial-languages');
actions.addRouteSharedData('/ja/docs/tutorials/', 'tutorial-languages', 'tutorial-languages');
actions.addRouteSharedData('/es-es/docs/tutorials/', 'tutorial-languages', 'tutorial-languages');
} catch (e) {
console.log('[tutorial-languages] Error:', e);
}
},
};
return instance;
}
/**
* Extract language names from tab labels in the markdown AST
*/
function extractLanguagesFromAst(ast) {
const languages = new Set();
visit(ast, (node) => {
// Look for tab nodes with a label attribute
if (isNode(node) && node.type === 'tag' && node.tag === 'tab') {
const label = node.attributes?.label;
if (label) {
const normalizedLang = normalizeLanguage(label);
if (normalizedLang) {
languages.add(normalizedLang);
}
}
}
});
return Array.from(languages);
}
/**
* Convert tab labels like "JavaScript", "Python", etc. to lowercase keys
* used for displaying the correct language icons on tutorial cards.
*/
function normalizeLanguage(label) {
const labelLower = label.toLowerCase();
if (labelLower.includes('javascript') || labelLower === 'js') {
return 'javascript';
}
if (labelLower.includes('python') || labelLower === 'py') {
return 'python';
}
if (labelLower.includes('java') && !labelLower.includes('javascript')) {
return 'java';
}
if (labelLower.includes('php')) {
return 'php';
}
if (labelLower.includes('go') || labelLower === 'golang') {
return 'go';
}
if (labelLower.includes('http') || labelLower.includes('websocket')) {
return 'http';
}
return null;
}
function isNode(value) {
return !!(value?.$$mdtype === 'Node');
}
function visit(node, visitor) {
if (!node) return;
visitor(node);
if (node.children) {
for (const child of node.children) {
if (!child || typeof child === 'string') {
continue;
}
visit(child, visitor);
}
}
}

View File

@@ -1,29 +0,0 @@
---
seo:
description: Learn how to get started building on the XRP Ledger with these helpful crypto wallet and blockchain tutorials for developers.
---
# Crypto Wallet and Blockchain Development Tutorials
The XRP Ledger tutorials walk you through the steps to learn and get started with the XRP Ledger and to use the ledger for advanced use cases.
## Get Started with SDKs
These tutorials walk you through the basics of building a very simple XRP Ledger-connected application using your favorite programming language.
{% card-grid %}
{% xrpl-card title="Javascript" body="Using the xrpl.js client library." href="/docs/tutorials/get-started/get-started-javascript/" image="/img/logos/javascript.svg" imageAlt="Javascript logo" /%}
{% xrpl-card title="Python" body="Using xrpl.py, a pure Python library." href="/docs/tutorials/get-started/get-started-python/" image="/img/logos/python.svg" imageAlt="Python logo" /%}
{% xrpl-card title="Go" body="Using xrpl-go, a pure Go library." href="/docs/tutorials/get-started/get-started-go/" image="/img/logos/golang.svg" imageAlt="Go logo" /%}
{% xrpl-card title="Java" body="Using xrpl4j, a pure Java library." href="/docs/tutorials/get-started/get-started-java/" image="/img/logos/java.svg" imageAlt="Java logo" /%}
{% xrpl-card title="PHP" body="Using the XRPL_PHP client library." href="/docs/tutorials/get-started/get-started-php/" image="/img/logos/php.svg" imageAlt="PHP logo" /%}
{% xrpl-card title="HTTP & WebSocket APIs" body="Access the XRP Ledger directly through the APIs of its core server." href="/docs/tutorials/get-started/get-started-http-websocket-apis/" image="/img/logos/globe.svg" imageAlt="globe icon" /%}
{% /card-grid %}
<!-- TODO: Add new sections for the new tutorial categories -->

View File

@@ -0,0 +1,353 @@
import { useThemeHooks } from "@redocly/theme/core/hooks";
export const frontmatter = {
seo: {
title: "Tutorials",
description:
"Learn how to get started building on the XRP Ledger with these helpful crypto wallet and blockchain tutorials for developers.",
},
};
const langIcons: Record<string, { src: string; alt: string }> = {
javascript: { src: "/img/logos/javascript.svg", alt: "JavaScript" },
python: { src: "/img/logos/python.svg", alt: "Python" },
java: { src: "/img/logos/java.svg", alt: "Java" },
go: { src: "/img/logos/golang.svg", alt: "Go" },
php: { src: "/img/logos/php.svg", alt: "PHP" },
http: { src: "/img/logos/globe.svg", alt: "HTTP / WebSocket" },
xrpl: { src: "/img/logos/xrp-mark.svg", alt: "XRP Ledger" },
};
// Type for the tutorial languages map from the plugin
type TutorialLanguagesMap = Record<string, string[]>;
interface Tutorial {
title: string;
body?: string;
path: string;
icon?: string; // Single language icon (for single-language tutorials)
}
interface TutorialSection {
id: string;
title: string;
description: string;
tutorials: Tutorial[];
}
// Get Started tutorials -----------------
const getStartedTutorials: Tutorial[] = [
{
title: "JavaScript",
body: "Using the xrpl.js client library.",
path: "/docs/tutorials/get-started/get-started-javascript/",
icon: "javascript",
},
{
title: "Python",
body: "Using xrpl.py, a pure Python library.",
path: "/docs/tutorials/get-started/get-started-python/",
icon: "python",
},
{
title: "Go",
body: "Using xrpl-go, a pure Go library.",
path: "/docs/tutorials/get-started/get-started-go/",
icon: "go",
},
{
title: "Java",
body: "Using xrpl4j, a pure Java library.",
path: "/docs/tutorials/get-started/get-started-java/",
icon: "java",
},
{
title: "PHP",
body: "Using the XRPL_PHP client library.",
path: "/docs/tutorials/get-started/get-started-php/",
icon: "php",
},
{
title: "HTTP & WebSocket APIs",
body: "Access the XRP Ledger directly through the APIs of its core server.",
path: "/docs/tutorials/get-started/get-started-http-websocket-apis/",
icon: "http",
},
];
// Other tutorial sections -----------------
// Languages are auto-detected from the markdown files by the tutorial-languages plugin.
// Only specify `icon` for single-language tutorials without tabs.
const sections: TutorialSection[] = [
{
id: "tokens",
title: "Tokens",
description: "Create and manage tokens on the XRP Ledger.",
tutorials: [
{
title: "Issue a Multi-Purpose Token",
body: "Issue new tokens using the v2 fungible token standard.",
path: "/docs/tutorials/tokens/mpts/issue-a-multi-purpose-token/",
},
{
title: "Issue a Fungible Token",
body: "Issue new tokens using the v1 fungible token standard.",
path: "/docs/tutorials/tokens/fungible-tokens/issue-a-fungible-token/",
},
{
title: "Mint and Burn NFTs Using JavaScript",
body: "Create new NFTs, retrieve existing tokens, and burn the ones you no longer need.",
path: "/docs/tutorials/tokens/nfts/mint-and-burn-nfts-js/",
icon: "javascript",
},
],
},
{
id: "payments",
title: "Payments",
description: "Transfer XRP and issued currencies using various payment types.",
tutorials: [
{
title: "Send XRP",
body: "Send a direct XRP payment to another account.",
path: "/docs/tutorials/payments/send-xrp/",
},
{
title: "Sending MPTs in JavaScript",
body: "Send a Multi-Purpose Token (MPT) to another account with the JavaScript SDK.",
path: "/docs/tutorials/tokens/mpts/sending-mpts-in-javascript/",
icon: "javascript",
},
{
title: "Create Trust Line and Send Currency in JavaScript",
body: "Set up trust lines and send issued currencies with the JavaScript SDK.",
path: "/docs/tutorials/payments/create-trust-line-send-currency-in-javascript/",
icon: "javascript",
},
{
title: "Create Trust Line and Send Currency in Python",
body: "Set up trust lines and send issued currencies with the Python SDK.",
path: "/docs/tutorials/payments/create-trust-line-send-currency-in-python/",
icon: "python",
},
{
title: "Send a Conditional Escrow",
body: "Send an escrow that can be released when a specific crypto-condition is fulfilled.",
path: "/docs/tutorials/payments/send-a-conditional-escrow/",
},
{
title: "Send a Timed Escrow",
body: "Send an escrow whose only condition for release is that a specific time has passed.",
path: "/docs/tutorials/payments/send-a-timed-escrow/",
},
],
},
{
id: "defi",
title: "DeFi",
description: "Trade, provide liquidity, and lend using native XRP Ledger DeFi features.",
tutorials: [
{
title: "Create an Automated Market Maker",
body: "Set up an AMM for a token pair and provide liquidity.",
path: "/docs/tutorials/defi/dex/create-an-automated-market-maker/",
},
{
title: "Trade in the Decentralized Exchange",
body: "Buy and sell tokens in the Decentralized Exchange (DEX).",
path: "/docs/tutorials/defi/dex/trade-in-the-decentralized-exchange/",
},
{
title: "Create a Loan Broker",
body: "Set up a loan broker to create and manage loans.",
path: "/docs/tutorials/defi/lending/use-the-lending-protocol/create-a-loan-broker/",
},
{
title: "Create a Loan",
body: "Create a loan on the XRP Ledger.",
path: "/docs/tutorials/defi/lending/use-the-lending-protocol/create-a-loan/",
},
{
title: "Create a Single Asset Vault",
body: "Create a single asset vault on the XRP Ledger.",
path: "/docs/tutorials/defi/lending/use-single-asset-vaults/create-a-single-asset-vault/",
},
{
title: "Deposit into a Vault",
body: "Deposit assets into a vault and receive shares.",
path: "/docs/tutorials/defi/lending/use-single-asset-vaults/deposit-into-a-vault/",
},
],
},
{
id: "best-practices",
title: "Best Practices",
description: "Learn recommended patterns for building reliable, secure applications on the XRP Ledger.",
tutorials: [
{
title: "API Usage",
body: "Best practices for using XRP Ledger APIs.",
path: "/docs/tutorials/best-practices/api-usage/",
},
{
title: "Use Tickets",
body: "Use tickets to send transactions out of the normal order.",
path: "/docs/tutorials/best-practices/transaction-sending/use-tickets/",
},
{
title: "Send a Single Account Batch Transaction",
body: "Group multiple transactions together and execute them as a single atomic operation.",
path: "/docs/tutorials/best-practices/transaction-sending/send-a-single-account-batch-transaction/",
},
{
title: "Assign a Regular Key Pair",
body: "Assign a regular key pair for signing transactions.",
path: "/docs/tutorials/best-practices/key-management/assign-a-regular-key-pair/",
},
{
title: "Set Up Multi-Signing",
body: "Configure multi-signing for enhanced security.",
path: "/docs/tutorials/best-practices/key-management/set-up-multi-signing/",
},
{
title: "Send a Multi-Signed Transaction",
body: "Send a transaction with multiple signatures.",
path: "/docs/tutorials/best-practices/key-management/send-a-multi-signed-transaction/",
},
],
},
{
id: "sample-apps",
title: "Sample Apps",
description: "Build complete, end-to-end applications like wallets and credential services.",
tutorials: [
{
title: "Build a Browser Wallet in JavaScript",
body: "Build a browser wallet for the XRP Ledger using JavaScript and various libraries.",
path: "/docs/tutorials/sample-apps/build-a-browser-wallet-in-javascript/",
icon: "javascript",
},
{
title: "Build a Desktop Wallet in JavaScript",
body: "Build a desktop wallet for the XRP Ledger using JavaScript, the Electron Framework, and various libraries.",
path: "/docs/tutorials/sample-apps/build-a-desktop-wallet-in-javascript/",
icon: "javascript",
},
{
title: "Build a Desktop Wallet in Python",
body: "Build a desktop wallet for the XRP Ledger using Python and various libraries.",
path: "/docs/tutorials/sample-apps/build-a-desktop-wallet-in-python/",
icon: "python",
},
{
title: "Credential Issuing Service in JavaScript",
body: "Build a credential issuing service using the JavaScript SDK.",
path: "/docs/tutorials/sample-apps/credential-issuing-service-in-javascript/",
icon: "javascript",
},
{
title: "Credential Issuing Service in Python",
body: "Build a credential issuing service using the Python SDK.",
path: "/docs/tutorials/sample-apps/credential-issuing-service-in-python/",
icon: "python",
},
],
},
];
function TutorialCard({
tutorial,
detectedLanguages,
showFooter = false,
translate,
}: {
tutorial: Tutorial;
detectedLanguages?: string[];
showFooter?: boolean;
translate: (text: string) => string;
}) {
// Get icons: auto-detected languages take priority, then manual icon, then XRPL fallback
const icons = detectedLanguages && detectedLanguages.length > 0
? detectedLanguages.map((lang) => langIcons[lang]).filter(Boolean)
: tutorial.icon && langIcons[tutorial.icon]
? [langIcons[tutorial.icon]]
: [langIcons.xrpl];
return (
<a href={tutorial.path} className="card">
<div className="card-header d-flex align-items-center flex-wrap">
{icons.map((icon, idx) => (
<span className="circled-logo" key={idx}>
<img src={icon.src} alt={icon.alt} />
</span>
))}
</div>
<div className="card-body">
<h4 className="card-title h5">{translate(tutorial.title)}</h4>
{tutorial.body && <p className="card-text">{translate(tutorial.body)}</p>}
</div>
{showFooter && <div className="card-footer"></div>}
</a>
);
}
export default function TutorialsIndex() {
const { useTranslate, usePageSharedData } = useThemeHooks();
const { translate } = useTranslate();
// Get auto-detected languages from the plugin (maps tutorial paths to language arrays).
const tutorialLanguages = usePageSharedData<TutorialLanguagesMap>("tutorial-languages") || {};
return (
<main className="landing page-tutorials landing-builtin-bg">
<section className="container-new py-26">
<div className="col-lg-8 mx-auto text-lg-center">
<div className="d-flex flex-column-reverse">
<h1 className="mb-0">
{translate("Crypto Wallet and Blockchain Development Tutorials")}
</h1>
<h6 className="eyebrow mb-3">{translate("Tutorials")}</h6>
</div>
</div>
</section>
{/* Get Started */}
<section className="container-new pt-10 pb-20">
<div className="col-12 col-xl-8 p-0">
<h3 className="h4 mb-3">{translate("Get Started with SDKs")}</h3>
<p className="mb-4">
{translate("These tutorials walk you through the basics of building a very simple XRP Ledger-connected application using your favorite programming language.")}
</p>
</div>
<div className="row tutorial-cards">
{getStartedTutorials.map((tutorial, idx) => (
<div key={idx} className="col-lg-4 col-md-6 mb-5">
<TutorialCard tutorial={tutorial} showFooter translate={translate} />
</div>
))}
</div>
</section>
{/* Other Tutorials */}
{sections.map((section) => (
<section className="container-new pt-10 pb-10" key={section.id}>
<div className="col-12 col-xl-8 p-0">
<h3 className="h4 mb-3">{translate(section.title)}</h3>
<p className="mb-4">{translate(section.description)}</p>
</div>
<div className="row tutorial-cards">
{section.tutorials.slice(0, 6).map((tutorial, idx) => (
<div key={idx} className="col-lg-4 col-md-6 mb-5">
<TutorialCard
tutorial={tutorial}
detectedLanguages={tutorialLanguages[tutorial.path]}
translate={translate}
/>
</div>
))}
</div>
</section>
))}
</main>
);
}

View File

@@ -127,7 +127,7 @@ footer:
- page: docs/concepts/index.md
label: Concepts
labelTranslationKey: footer.docs.concepts
- page: docs/tutorials/index.md
- page: docs/tutorials/index.page.tsx
label: Tutorials
labelTranslationKey: footer.docs.tutorials
- page: docs/references/index.md

View File

@@ -184,7 +184,7 @@
- page: docs/concepts/decentralized-storage/decentralized-identifiers.md
- page: docs/concepts/decentralized-storage/price-oracles.md
- page: docs/tutorials/index.md
- page: docs/tutorials/index.page.tsx
label: Tutorials
labelTranslationKey: sidebar.docs.tutorials
expanded: false

File diff suppressed because one or more lines are too long

View File

@@ -320,3 +320,84 @@ main article .card-grid {
margin-bottom: 0.25rem;
margin-top: 0.5rem;
}
/* Tutorial cards */
.page-tutorials .tutorial-cards {
> div {
display: flex;
}
.card {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
min-height: 280px;
transition: all 0.35s ease-out;
cursor: pointer;
&:hover {
transform: translateY(-16px);
}
.card-header {
border-bottom: none;
background: transparent;
padding: 1.5rem 1.5rem 0 2rem;
}
.circled-logo {
margin-left: -10px;
display: inline-flex;
align-items: center;
justify-content: center;
}
.card-body {
padding: 1rem 1.5rem;
flex: 1;
}
.card-title {
font-weight: 700;
margin-bottom: 1rem;
}
.card-text {
font-size: 1rem;
margin-bottom: 1.5rem;
}
.card-footer {
background-color: transparent;
border-top: none;
padding: 0;
height: 40px;
background-size: cover;
background-position: bottom;
background-repeat: no-repeat;
margin-top: auto;
}
}
// Apply colored footers to each card
> div:nth-child(1) .card .card-footer {
background-image: url("../img/cards/3-col-pink.svg");
}
> div:nth-child(2) .card .card-footer {
background-image: url("../img/cards/3col-blue-light-blue.svg");
}
> div:nth-child(3) .card .card-footer {
background-image: url("../img/cards/3-col-light-blue.svg");
}
> div:nth-child(4) .card .card-footer {
background-image: url("../img/cards/3col-blue-green.svg");
}
> div:nth-child(5) .card .card-footer {
background-image: url("../img/cards/3col-magenta.svg");
}
> div:nth-child(6) .card .card-footer {
background-image: url("../img/cards/3-col-orange.svg");
}
}

View File

@@ -424,7 +424,7 @@ aside .card {
.circled-logo {
background-color: $gray-200;
img[src="assets/img/logos/globe.svg"] {
img[src="/img/logos/globe.svg"] {
filter: invert(100%);
}
}

View File

@@ -60,7 +60,7 @@
- page: ./docs/concepts/index.md
label: Concepts
labelTranslationKey: topnav.docs.concepts
- page: ./docs/tutorials/index.md
- page: ./docs/tutorials/index.page.tsx
label: Tutorials
labelTranslationKey: topnav.docs.tutorials
- page: ./docs/references/index.md