Skip to content

Commit add20f3

Browse files
committed
feat: enhance RPC client documentation and UX
- Add integration-focused navigation structure - Improve RPC playground widget with better responsive layout - Add info popover for default server limitations - Simplify history display by removing grouping - Enhance styling for code snippets and timestamps - Add NqHeadline component and integration pages - Update method index with search functionality - Fix syntax issues and linting errors
1 parent 7bce2e3 commit add20f3

File tree

17 files changed

+551
-292
lines changed

17 files changed

+551
-292
lines changed

.vitepress/theme.config.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,22 @@ export const themeConfig = {
156156
label: 'Overview',
157157
items: [
158158
{ text: 'Overview', link: '/rpc-client/', icon: 'i-tabler:layout-grid' },
159-
{ text: 'Clients', link: '/rpc-client/clients', icon: 'i-tabler:plug' },
159+
{ text: 'Integrations', icon: 'i-tabler:chart-funnel', items: [
160+
{ text: 'Raw Requests', link: '/rpc-client/integrations/raw', icon: 'i-tabler:terminal-2' },
161+
{ text: 'JavaScript Native', link: '/rpc-client/integrations/javascript', icon: 'i-logos:javascript' },
162+
{ text: 'TypeScript Client', link: '/rpc-client/integrations/typescript', icon: 'i-logos:typescript-icon' },
163+
{ text: 'ARPL CLI Tool', link: '/rpc-client/integrations/arpl', icon: 'i-tabler:terminal' },
164+
{ text: 'MCP Server (AI)', link: '/rpc-client/integrations/mcp', icon: 'i-tabler:robot' },
165+
166+
] },
160167
],
161168
},
162169
{
163170
label: 'Methods',
164-
items: [...(await loadMethods(openRpcDocument as OpenrpcDocument))],
171+
items: [
172+
{ text: 'All', link: '/rpc-client/methods/', icon: 'i-tabler:grid-dots' },
173+
...(await loadMethods(openRpcDocument as OpenrpcDocument)),
174+
],
165175
},
166176
],
167177
},
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<script setup lang="ts">
2+
import { useData } from 'vitepress'
3+
import { computed } from 'vue'
4+
5+
const props = defineProps<{
6+
title: string
7+
description: string
8+
label?: string
9+
h1?: boolean
10+
align?: 'center' | 'left'
11+
}>()
12+
13+
const { frontmatter, page } = useData()
14+
15+
const title = computed(() => props.title || frontmatter.value.title)
16+
const description = computed(() => props.description || frontmatter.value.description)
17+
18+
const effectiveAlign = computed(() => {
19+
if (props.align) {
20+
return props.align
21+
}
22+
23+
return page.value.frontmatter.layout === 'home' ? 'center' : 'left'
24+
})
25+
26+
const h1 = computed(() => props.h1 ?? true)
27+
</script>
28+
29+
<template>
30+
<div
31+
flex="~ col" nq-component="headline"
32+
:class="{ 'items-start': effectiveAlign === 'left',
33+
'items-center': effectiveAlign === 'center',
34+
'f-mt-2xl': !h1 }" class="nq-raw"
35+
f-mb-2xl
36+
>
37+
<div
38+
v-if="label" outline="~ 1.5 neutral-600" bg="neutral/3" px-12 py-6 rounded-full nq-label
39+
:class="effectiveAlign === 'left' ? 'text-left' : 'text-center'"
40+
>
41+
{{ label }}
42+
</div>
43+
<component
44+
:is="h1 ? 'h1' : 'h2'" :class="[{
45+
'f-mt-xs': label,
46+
},
47+
h1 ? 'f-text-4xl font-bold' : 'f-text-3xl font-semibold',
48+
effectiveAlign === 'left' ? 'text-left' : 'text-center',
49+
]" text-neutral mb-0
50+
>
51+
{{ title }}
52+
</component>
53+
<p
54+
:class="[{
55+
'mt-0 f-text-2xl': h1,
56+
'f-mt-2xs f-text-xl': !h1,
57+
}, effectiveAlign === 'left' ? 'text-left' : 'text-center']"
58+
>
59+
{{ description }}
60+
</p>
61+
</div>
62+
</template>

.vitepress/theme/components/Rpc/RpcMethod.vue

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const { tabs, currentTab } = useCodeSnippet(widget)
2121
{{ props.description }}
2222
</p>
2323

24-
<div grid="~ cols-[32ch_1fr] gap-32" :class="{ 'f-mt-lg': !props.description }">
24+
<div :class="{ 'f-mt-lg': !props.description }">
2525
<div mx-0>
2626
<h2 nq-label mt-0="!">
2727
Params
@@ -48,11 +48,10 @@ const { tabs, currentTab } = useCodeSnippet(widget)
4848
</li>
4949
</ul>
5050
</div>
51-
<!-- .25em .65em .25em .75em -->
5251

53-
<Tabs.Root v-model="currentTab" class="tabs" outline="1.5 offset--1.5 solid neutral/8" rounded-8 h-max max-w-none min-w-0 w-full>
52+
<Tabs.Root v-model="currentTab" class="tabs" outline="1.5 offset--1.5 solid neutral/8" rounded-8 h-max max-w-none min-w-0 w-full f-mt-lg>
5453
<Tabs.List flex="~ justify-start gap-16" f-px="20/24" :aria-label="`See how to call ${widget.method}`" py-8 bg-neutral-50 h-44 border="b-1.5 solid neutral/8">
55-
<Tabs.Trigger v-for="({ icon, lang, label }) in tabs" :key="lang" :value="lang" bg="transparent reka-active:blue-400" flex="~ items-center gap-8" text="neutral-800 f-xs" font-bold mx-0 ml--8 px-0.65em py-4 rounded-6>
54+
<Tabs.Trigger v-for="({ icon, lang, label }) in tabs" :key="lang" :value="lang" bg="transparent reka-active:blue-400" flex="~ items-center gap-8" text="neutral-800 f-xs" font-bold mx-0 ml-2 px-0.65em py-4 rounded-6>
5655
<div :class="icon" grayscale="reka-active:0 100" transition-filter />
5756
<span reka-active:text-blue>
5857
{{ label }}
@@ -67,7 +66,7 @@ const { tabs, currentTab } = useCodeSnippet(widget)
6766

6867
<ClientOnly>
6968
<Teleport defer to="#widget">
70-
<RpcPlayground v-bind="props" />
69+
<RpcPlayground v-if="props" v-bind="props" />
7170
</Teleport>
7271
</ClientOnly>
7372
</div>

.vitepress/theme/components/Rpc/RpcPlayground.vue

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { usePlaygroundRpc } from './playground'
66
77
const props = defineProps<NimiqRpcMethod>()
88
9-
const { callRpc, widget, history, groupedHistory, clearHistory, playgroundConfig, defaultNodeUrl } = usePlaygroundRpc(props)
9+
const { callRpc, widget, history, clearHistory, playgroundConfig, defaultNodeUrl } = usePlaygroundRpc(props)
1010
1111
const formatterTime = new Intl.DateTimeFormat('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })
1212
const formatterWithDate = new Intl.DateTimeFormat('en-US', { year: '2-digit', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })
@@ -66,6 +66,25 @@ function formatTimestamp(timestamp: number, showDate: boolean = false) {
6666
</form>
6767
</div>
6868

69+
<!-- Info icon for default server limitations -->
70+
<div flex="~ items-center gap-8" f-mt-xs>
71+
<Popover.Root>
72+
<Popover.Trigger>
73+
<div text-16 text-neutral-600 cursor-pointer i-nimiq:info />
74+
</Popover.Trigger>
75+
<Popover.Portal>
76+
<Popover.Content :collision-padding="12" side="top" :side-offset="4" will-change="transform,opacity" reka-open="animate-in slide-in-b fade-in" reka-close="animate-out slide-out-t fade-out" outline="~ 1.5 offset--1.5 neutral-200" rounded-8 bg-neutral-0 max-w-320 w-max shadow relative f-p-xs>
77+
<p text-neutral-800 m-0 f-text-xs>
78+
The default RPC server may not support all methods. If you encounter errors, consider connecting to your own Nimiq node for full method availability.
79+
</p>
80+
<Popover.Close bg="neutral-200 hocus:neutral-300" outline="~ 1.5 offset--1.5 neutral/20" stack rounded-full transition right-8 top-8 absolute size-16="!">
81+
<div text-6 i-nimiq:cross />
82+
</Popover.Close>
83+
</Popover.Content>
84+
</Popover.Portal>
85+
</Popover.Root>
86+
</div>
87+
6988
<div class="nq-raw widget-container" f-mt-sm>
7089
<header flex="~ items-center gap-8">
7190
<div text-10 op-70 i-nimiq:watch-1-50 />
@@ -81,34 +100,27 @@ function formatTimestamp(timestamp: number, showDate: boolean = false) {
81100
Run the request to see the history...
82101
</p>
83102
<div v-else>
84-
<ol>
85-
<li v-for="({ items, label }) in groupedHistory.filter(item => item.items.length > 0)" :key="label">
86-
<h4 mb-4 nq-label>
87-
{{ label }}
88-
</h4>
89-
<Accordion.Root type="multiple" :collapsible="true">
90-
<Accordion.Item v-for="([isOk, error, data, { request: { timestamp, body } }], index) in items" :key="index" :value="`${timestamp}`">
91-
<Accordion.Trigger bg="transparent hocus:neutral-200 reka-open:neutral-200" flex="~ items-center gap-8" p-4 px-8 w-full rounded="4 data-open:b-0">
92-
<div text-6 op-80 shrink-0 transition-transform i-nimiq:chevron-right reka-open:rotate-90 />
93-
<code font-semibold text-ellipsis of-hidden f-text-2xs>
94-
{{ body.method }}
95-
</code>
96-
<div ml-auto flex="~ items-center gap-8" shrink-0>
97-
<div v-if="!isOk" stack p-3 rounded-2 bg-red-400 outline="1.5 ~ neutral-0/10" :title="error">
98-
<div v-if="error" text-12 text-red op-80 i-nimiq:alert />
99-
</div>
100-
<span text="9 neutral-700" font-semibold>
101-
{{ formatTimestamp(timestamp, label === 'Older') }}
102-
</span>
103-
</div>
104-
</Accordion.Trigger>
105-
<Accordion.Content un-animate-accordion="reka-open:down reka-closed:up" :value="timestamp" outline="1.5 neutral-200 ~ offset--1.5" rounded-b-4 bg-neutral-50 of-hidden f-mb-2xs>
106-
<pre py-4 rounded-8 bg-neutral-50 of-x-auto f-text-2xs f-px-2xs>{{ isOk ? data : error }}</pre>
107-
</Accordion.Content>
108-
</Accordion.Item>
109-
</Accordion.Root>
110-
</li>
111-
</ol>
103+
<Accordion.Root type="multiple" :collapsible="true">
104+
<Accordion.Item v-for="([isOk, error, data, { request: { timestamp, body } }], index) in history" :key="index" :value="`${timestamp}`">
105+
<Accordion.Trigger bg="transparent hocus:neutral-200 reka-open:neutral-200" flex="~ items-center gap-8" p-4 px-8 w-full rounded="4 data-open:b-0">
106+
<div text-6 op-80 shrink-0 transition-transform i-nimiq:chevron-right reka-open:rotate-90 />
107+
<code font-semibold text-ellipsis of-hidden f-text-2xs>
108+
{{ body.method }}
109+
</code>
110+
<div ml-auto flex="~ items-center gap-8" shrink-0>
111+
<div v-if="!isOk" stack p-3 rounded-2 bg-red-400 outline="1.5 ~ neutral-0/10" :title="error">
112+
<div v-if="error" text-12 text-red op-80 i-nimiq:alert />
113+
</div>
114+
<span text="9 neutral-700" font-semibold>
115+
{{ formatTimestamp(timestamp, true) }}
116+
</span>
117+
</div>
118+
</Accordion.Trigger>
119+
<Accordion.Content un-animate-accordion="reka-open:down reka-closed:up" :value="timestamp" outline="1.5 neutral-200 ~ offset--1.5" rounded-b-4 bg-neutral-50 of-hidden f-mb-2xs>
120+
<pre py-4 rounded-8 bg-neutral-50 of-x-auto f-text-2xs f-px-2xs>{{ isOk ? data : error }}</pre>
121+
</Accordion.Content>
122+
</Accordion.Item>
123+
</Accordion.Root>
112124
</div>
113125
</div>
114126
</div>

.vitepress/theme/components/Rpc/playground.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -82,32 +82,11 @@ export function usePlaygroundRpc(props: MaybeRef<Partial<NimiqRpcMethod>>) {
8282
}
8383
}
8484

85-
const groupedHistory = computed(() => {
86-
const today: HttpRpcResult<any>[] = []
87-
const yesterday: HttpRpcResult<any>[] = []
88-
const older: HttpRpcResult<any>[] = []
89-
90-
const now = new Date()
91-
for (const item of history.value) {
92-
const date = new Date(item[3].request.timestamp)
93-
const diff = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24))
94-
95-
if (diff === 0)
96-
today.push(item)
97-
else if (diff === 1)
98-
yesterday.push(item)
99-
else
100-
older.push(item)
101-
}
102-
return [{ label: 'Today', items: today }, { label: 'Yesterday', items: yesterday }, { label: 'Older', items: older }]
103-
})
104-
10585
return {
10686
defaultNodeUrl,
10787
widget: playground,
10888
callRpc,
10989
history,
110-
groupedHistory,
11190
clearHistory,
11291
playgroundConfig,
11392
}

.vitepress/theme/main.css

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,57 @@ div:has(+ article) {
4040
border: 0;
4141
outline: none;
4242
}
43+
44+
/* RPC Widget Sidebar Improvements */
45+
46+
/* Increase widget sidebar width on desktop for better widget display */
47+
@media (min-width: 1224px) {
48+
/* When widget is shown, use a wider secondary sidebar */
49+
[data-layout="docs"]:has(#widget:not(:empty)) {
50+
--nq-widget-sidebar-width: 480px;
51+
}
52+
53+
/* Apply wider width to the secondary sidebar container when widget is present */
54+
[data-layout="docs"]:has(#widget:not(:empty)) > div > div:last-child {
55+
width: var(--nq-widget-sidebar-width, 288px) !important;
56+
}
57+
58+
/* Adjust main content max-width when widget sidebar is wider to maintain layout balance */
59+
[data-layout="docs"]:has(#widget:not(:empty)) main {
60+
max-width: calc(1000px - (var(--nq-widget-sidebar-width, 288px) - 288px)) !important;
61+
}
62+
}
63+
64+
/* RPC Code Snippets Font Size Fix */
65+
66+
/* Increase font size for code snippets in RPC method tabs */
67+
.tabs pre.shiki {
68+
font-size: 0.875rem !important; /* Increased from f-text-xs (0.75rem) to 0.875rem */
69+
line-height: 1.5 !important;
70+
}
71+
72+
/* Also increase font size for inline code in RPC methods */
73+
.tabs code {
74+
font-size: 0.875rem !important;
75+
}
76+
77+
/* RPC History Timestamp Size Fix */
78+
79+
/* Increase font size for timestamps in RPC history */
80+
.widget-container span[font-semibold] {
81+
font-size: 0.75rem !important; /* Increased from text-9 (very small) to more readable size */
82+
}
83+
84+
/* RPC Warning Banner Styling */
85+
86+
/* Style the warning banner to connect seamlessly with the form above */
87+
.rpc-warning-banner {
88+
margin-top: 0 !important;
89+
border-top: none !important;
90+
border-radius: 0 0 8px 8px !important;
91+
}
92+
93+
/* Ensure the form container above has no bottom border radius */
94+
.widget-container:has(+ .rpc-warning-banner) {
95+
border-radius: 8px 8px 0 0 !important;
96+
}

.vitepress/vite-env.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { NimiqRpcMethods } from './rpc/utils'
66
declare global {
77
const __NIMIQ_OPENRPC_INFO__: OpenrpcDocument['info']
88
const __NIMIQ_OPENRPC_METHODS__: NimiqRpcMethods[]
9+
const __NIMIQ_RPC_VERSION__: string
910
}
1011

1112
export { }

.vitepress/vite.config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs'
22
import { consola } from 'consola'
33
import { NimiqVitepressVitePlugin } from 'nimiq-vitepress-theme/vite'
44
import { resolve } from 'pathe'
5+
import { readPackageJSON } from 'pkg-types'
56
import UnoCSS from 'unocss/vite'
67
import AutoImport from 'unplugin-auto-import/vite'
78
import Components from 'unplugin-vue-components/vite'
@@ -24,6 +25,10 @@ export default defineConfig(async () => {
2425
const openRpcDocInfo = openRpcDoc.info
2526
const methods = await loadMethods(openRpcDoc)
2627

28+
// Get the nimiq-rpc-client-ts version from package.json
29+
const { dependencies } = await readPackageJSON()
30+
const nimiqRpcVersion = dependencies?.['nimiq-rpc-client-ts'] || 'unknown'
31+
2732
return {
2833
optimizeDeps: {
2934
exclude: ['vitepress', '@nimiq/core'],
@@ -57,6 +62,7 @@ export default defineConfig(async () => {
5762
define: {
5863
__NIMIQ_OPENRPC_INFO__: JSON.stringify(openRpcDocInfo),
5964
__NIMIQ_OPENRPC_METHODS__: JSON.stringify(methods),
65+
__NIMIQ_RPC_VERSION__: JSON.stringify(nimiqRpcVersion),
6066
global: 'globalThis',
6167
},
6268

rpc-client/clients.md

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,13 @@
1-
# Nimiq RPC Clients
1+
# Client Integrations
22

3-
Connect to the Nimiq blockchain using your preferred programming language or AI assistant.
3+
The Nimiq RPC API provides multiple integration options for connecting to the blockchain. Choose the approach that best fits your development needs.
44

5-
## AI Assistant Integration
5+
Use the **Integrations** section in the sidebar to explore each client option, or jump directly to:
66

7-
### Nimiq MCP Server
8-
**For AI assistants like Claude, ChatGPT, and others**
7+
- **[Raw Requests](./integrations/raw)** - curl, wget, and direct HTTP calls
8+
- **[JavaScript Native](./integrations/javascript)** - Pure JavaScript with fetch API
9+
- **[TypeScript Client](./integrations/typescript)** - Fully typed production client
10+
- **[ARPL CLI Tool](./integrations/arpl)** - Command-line interface for node management
11+
- **[MCP Server (AI)](./integrations/mcp)** - AI assistant integration
912

10-
The [Nimiq Model Context Protocol (MCP) Server](https://github.com/onmax/nimiq-mcp) enables AI assistants to interact directly with the Nimiq blockchain through a standardized protocol.
11-
12-
- **Installation**: `npx nimiq-mcp`
13-
- **Features**: 20+ blockchain tools for accounts, transactions, validators, and documentation
14-
- **Use Cases**: AI-assisted development, automated analysis, smart contract interactions
15-
- **Documentation**: [GitHub Repository](https://github.com/onmax/nimiq-mcp)
16-
17-
#### Quick Setup for Claude Desktop
18-
```json
19-
{
20-
"mcpServers": {
21-
"nimiq": {
22-
"command": "npx",
23-
"args": ["nimiq-mcp"]
24-
}
25-
}
26-
}
27-
```
28-
29-
## Programming Language Clients
30-
31-
### TypeScript/JavaScript
32-
- **[nimiq-rpc-client-ts](https://github.com/onmax/albatross-rpc-client-ts)**: Fully typed RPC client for TypeScript and JavaScript
33-
- **Browser Compatible**: Works in both Node.js and browser environments
34-
- **WebSocket Support**: Real-time blockchain event subscriptions
35-
36-
## Community Clients
37-
38-
Missing your favorite language? We welcome community contributions for additional client libraries. Check our [development guidelines](https://github.com/nimiq) for creating new clients.
39-
40-
## Need Help?
41-
42-
- **Documentation**: [RPC API Reference](./methods/)
43-
- **Community**: [Telegram Developer Chat](https://t.me/nimiq)
44-
- **GitHub**: [Nimiq Organization](https://github.com/nimiq)
45-
- **Issues**: Report bugs or request features on [GitHub Issues](https://github.com/nimiq/core-rs-albatross/issues)
13+
For complete method documentation: **[Browse all RPC methods →](./methods/)**

0 commit comments

Comments
 (0)