From blueprint-frontend
Use when building frontend applications for Tangle Blueprints — job submission, operator discovery, service provisioning, session auth, or integrating @tangle-network/blueprint-ui.
How this skill is triggered — by the user, by Claude, or both
Slash command
/blueprint-frontend:blueprint-frontendThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill when building React frontends for Tangle Network blueprints. Covers the shared UI library (`@tangle-network/blueprint-ui`), on-chain interaction patterns, session auth, and Web3 provider setup.
Use this skill when building React frontends for Tangle Network blueprints. Covers the shared UI library (@tangle-network/blueprint-ui), on-chain interaction patterns, session auth, and Web3 provider setup.
For sandbox-specific frontend patterns (agent chat, terminal, sidecar auth), see sandbox-blueprint.
@tangle-network/blueprint-uiChain/contract interaction, job forms, stores, layout primitives. App-agnostic — no product-specific routing or copy.
Source: tangle-network/blueprint-ui
Three export entry points:
@tangle-network/blueprint-ui — hooks, stores, contracts, utilities@tangle-network/blueprint-ui/components — UI components@tangle-network/blueprint-ui/preset — UnoCSS theme tokensRegister blueprints at app startup to enable generic job forms and submission:
import { registerBlueprint, type BlueprintDefinition, type JobDefinition } from '@tangle-network/blueprint-ui';
const MY_JOBS: JobDefinition[] = [
{
id: 0,
name: 'create_instance',
label: 'Create Instance',
description: 'Provision a new sandbox instance',
category: 'lifecycle',
icon: 'i-ph:play',
pricingMultiplier: 1.0,
requiresSandbox: false,
fields: [
{
name: 'name',
label: 'Instance Name',
type: 'text',
required: true,
abiType: 'string',
abiParam: 'name',
},
{
name: 'cpu_cores',
label: 'CPU Cores',
type: 'number',
defaultValue: 2,
min: 1,
max: 16,
abiType: 'uint64',
abiParam: 'cpu_cores',
},
{
name: 'runtime_backend',
label: 'Runtime',
type: 'select',
defaultValue: 'docker',
options: [
{ label: 'Docker', value: 'docker' },
{ label: 'Firecracker', value: 'firecracker' },
],
abiType: 'string',
abiParam: 'runtime_backend',
},
],
},
];
const MY_BLUEPRINT: BlueprintDefinition = {
id: 'my-blueprint',
name: 'My Blueprint',
version: '1.0.0',
description: 'Description',
icon: 'i-ph:cube',
color: '#3B82F6',
contracts: {
31337: '0x...', // local
3799: '0x...', // testnet
},
jobs: MY_JOBS,
categories: [
{ key: 'lifecycle', label: 'Lifecycle', icon: 'i-ph:arrows-clockwise' },
],
};
registerBlueprint(MY_BLUEPRINT);
type JobCategory = 'lifecycle' | 'execution' | 'batch' | 'workflow' | 'ssh' | 'management';
interface JobFieldDef {
name: string;
label: string;
type: 'text' | 'textarea' | 'number' | 'boolean' | 'select' | 'json' | 'combobox';
required?: boolean;
defaultValue?: string | number | boolean;
options?: { label: string; value: string }[];
helperText?: string;
min?: number; max?: number; step?: number;
abiType?: string; // e.g. 'uint64', 'string', 'address[]'
abiParam?: string; // Solidity param name
internal?: boolean; // included in encoding but hidden from form
}
interface JobDefinition {
id: number;
name: string;
label: string;
description: string;
category: JobCategory;
icon: string;
pricingMultiplier: number;
fields: JobFieldDef[];
requiresSandbox: boolean;
warning?: string;
contextParams?: AbiContextParam[]; // e.g. sidecar_url, sandbox_id
customEncoder?: (values, context?) => `0x${string}`; // for nested structs
}
useOperators() → discover operators for blueprint
↓
useQuotes() → fetch RFQ pricing from operators
↓
useJobForm() → manage form state from JobDefinition
↓
encodeJobArgs() → ABI-encode form values using field metadata
↓
useSubmitJob() → submit on-chain, track TX lifecycle
↓
useProvisionProgress() → poll provision status until ready
import { useSubmitJob } from '@tangle-network/blueprint-ui';
const { submitJob, status, txHash, callId, error, reset } = useSubmitJob();
await submitJob({
serviceId: 1n,
jobId: 0, // matches JobDefinition.id
args: encodeJobArgs(job, formValues, context),
label: 'Create Instance',
value: quotedPrice, // optional payment
});
// status: 'idle' → 'signing' → 'pending' → 'confirmed' | 'failed'
// callId: extracted from JobCalled event logs
import { useOperators } from '@tangle-network/blueprint-ui';
const { operators, operatorCount, isLoading, error } = useOperators();
// operators: { address, ecdsaPublicKey, rpcAddress }[]
import { useQuotes, formatCost } from '@tangle-network/blueprint-ui';
const { quotes, totalCost, isLoading, isSolvingPow } = useQuotes(
operators,
blueprintId,
ttlBlocks,
enabled,
);
// quotes: { operator, totalCost, signature, details, costRate }[]
// Internally solves 20-bit SHA256 PoW before requesting quote
import { useJobPrice } from '@tangle-network/blueprint-ui';
const { quote, formattedPrice, isLoading } = useJobPrice(
operatorRpcUrl,
serviceId,
jobIndex,
blueprintId,
enabled,
);
import { encodeJobArgs } from '@tangle-network/blueprint-ui';
const encoded = encodeJobArgs(jobDefinition, formValues, {
sidecar_url: 'http://...',
sandbox_id: '0x...',
});
// Returns ABI-encoded 0x... string
// Handles coercion: bools, uintX, strings, arrays
// Delegates to job.customEncoder for complex structs
import { useServiceValidation } from '@tangle-network/blueprint-ui';
const { validate, serviceInfo, isValidating, error } = useServiceValidation();
const info = await validate(serviceId, userAddress);
// info: { active, blueprintId, owner, operatorCount, operators, permitted, ttl, createdAt }
import { useProvisionProgress, getPhaseLabel } from '@tangle-network/blueprint-ui';
const { phase, progressPct, sandboxId, sidecarUrl, isReady, isFailed, message } =
useProvisionProgress(callId, operatorRpcUrl, enabled);
// Phases: queued → image_pull → container_create → container_start → health_check → ready | failed
// Polls every 2s, stops on terminal phase
import { useSessionAuth, useAuthenticatedFetch } from '@tangle-network/blueprint-ui';
const { session, isAuthenticated, authenticate, logout } = useSessionAuth(sandboxId, operatorUrl);
// Challenge-response: request challenge → sign with wallet → exchange for PASETO token
// Stored in sessionMapStore (persisted to localStorage, keyed by sandboxId)
const { authFetch } = useAuthenticatedFetch(sandboxId, operatorUrl);
const res = await authFetch('/api/instances');
// Auto-injects Bearer token, re-authenticates on 401
import { infraStore, updateInfra, getInfra } from '@tangle-network/blueprint-ui';
updateInfra({ blueprintId: '1', serviceId: '1' });
const { blueprintId, serviceId, serviceInfo } = getInfra();
// Persisted to localStorage
import { txListStore, addTx, updateTx, pendingCount } from '@tangle-network/blueprint-ui';
addTx({ hash, label: 'Create Instance', status: 'pending', chainId });
// Max 50 TXs, BigInt-aware serialization
import { getSession, setSession, removeSession, gcSessions } from '@tangle-network/blueprint-ui';
const session = getSession(sandboxId);
// { token, address, expiresAt, sandboxId }
// Auto-cleans expired sessions (60s buffer)
import { Web3Shell } from '@tangle-network/blueprint-ui/components';
import { tangleWalletChains, createTangleTransports, defaultConnectKitOptions } from '@tangle-network/blueprint-ui';
import { createConfig, WagmiProvider } from 'wagmi';
import { getDefaultConfig } from 'connectkit';
const config = createConfig(
getDefaultConfig({
chains: tangleWalletChains, // [tangleLocal, tangleTestnet, tangleMainnet, mainnet]
transports: createTangleTransports(),
walletConnectProjectId: import.meta.env.VITE_WALLETCONNECT_PROJECT_ID,
...defaultConnectKitOptions,
}),
);
// Web3Shell wraps with WagmiProvider + QueryClientProvider
<Web3Shell config={config}>
<App />
</Web3Shell>
import {
tangleLocal, tangleTestnet, tangleMainnet,
configureNetworks, getNetworks, resolveRpcUrl,
selectedChainIdStore, getPublicClient, getAddresses,
} from '@tangle-network/blueprint-ui';
// Register networks with contract addresses
configureNetworks([
{ chain: tangleLocal, rpcUrl: resolveRpcUrl(), label: 'Local', addresses: { jobs: '0x...', services: '0x...' } },
{ chain: tangleTestnet, rpcUrl: 'https://testnet-rpc.tangle.tools', label: 'Testnet', addresses: { ... } },
]);
// Chain switching updates publicClient automatically
selectedChainIdStore.set(3799); // switch to testnet
const client = getPublicClient();
const { jobs, services } = getAddresses();
import {
AppDocument, Web3Shell, ChainSwitcher, ThemeToggle, AppToaster, AnimatedPage,
} from '@tangle-network/blueprint-ui/components';
// AppDocument: sets <html data-theme>, prevents FOUC, preloads fonts
// ChainSwitcher: dropdown for Local/Testnet/Mainnet
// ThemeToggle: dark/light toggle
// AppToaster: sonner toast integration
// AnimatedPage: framer-motion page transitions
import { BlueprintJobForm, JobExecutionDialog } from '@tangle-network/blueprint-ui/components';
import { useJobForm } from '@tangle-network/blueprint-ui';
// Manual form rendering:
const { values, errors, onChange, validate } = useJobForm(jobDefinition);
<BlueprintJobForm job={jobDefinition} values={values} onChange={onChange} errors={errors} />
// Complete dialog (form + pricing + submission):
<JobExecutionDialog
open={open}
onOpenChange={setOpen}
job={jobDefinition}
serviceId={serviceId}
context={{ sandbox_id: '0x...' }}
onSuccess={(callId) => watchProvision(callId)}
/>
import { bpThemeTokens } from '@tangle-network/blueprint-ui/preset';
// UnoCSS config:
export default defineConfig({
theme: {
colors: {
bp: bpThemeTokens('myapp'),
},
},
});
// Usage in components:
// text-bp-elements-textPrimary
// bg-bp-elements-background-depth-1
// border-bp-elements-borderColor
| Variable | Purpose |
|---|---|
VITE_CHAIN_ID | Default chain (31337 local, 3799 testnet, 5845 mainnet) |
VITE_RPC_URL | RPC endpoint (defaults to localhost:8545) |
VITE_BLUEPRINT_ID | Default blueprint ID |
VITE_SERVICE_ID | Default service ID |
VITE_OPERATOR_API_URL | Operator API endpoint |
VITE_WALLETCONNECT_PROJECT_ID | WalletConnect project ID |
VITE_OPERATOR_API_TOKEN | Operator bearer token (dev) |
On-chain (state-changing, via useSubmitJob):
Off-chain (operator HTTP API, via useAuthenticatedFetch):
Jobs mutate state. Everything else goes through the operator API.
src/index.ts — main exports (hooks, stores, contracts, utils)src/components.ts — component exportssrc/preset.ts — UnoCSS theme tokenssrc/hooks/useSubmitJob.ts — job submissionsrc/hooks/useOperators.ts — operator discoverysrc/hooks/useQuotes.ts — RFQ pricing with PoWsrc/hooks/useJobPrice.ts — per-job pricingsrc/hooks/useServiceValidation.ts — service validationsrc/hooks/useSessionAuth.ts — PASETO session managementsrc/hooks/useProvisionProgress.ts — provision trackingsrc/hooks/useJobForm.ts — form state managementsrc/blueprints/registry.ts — blueprint registrationsrc/contracts/abi.ts — Tangle contract ABIssrc/contracts/chains.ts — chain definitionssrc/contracts/publicClient.ts — reactive public clientsrc/contracts/generic-encoder.ts — ABI argument encodingsrc/stores/ — infraStore, sessionMapStore, txListStore, themeStoresrc/components/forms/BlueprintJobForm.tsx — job form renderersrc/components/forms/JobExecutionDialog.tsx — complete submission dialogeth_call and operator HTTP API.encodeJobArgs for ABI encoding. Don't hand-roll encoding from form values.gcSessions() or rely on sessionMapStore's built-in cleanup.npx claudepluginhub tangle-network/skills --plugin blueprint-frontendProvides Ritual dApp design system with dark-mode-first Tailwind config, color tokens, typography, async precompile lifecycle visuals, icons, and AI slop prevention for React/Next.js frontends.
Builds Telegram Mini Apps (TWA) with the Telegram Web App API, TON blockchain integration, and viral monetization techniques. Guides UX patterns and user authentication.