@tangle-network/blueprint-ui 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/blueprint-ui",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Shared blueprint UI components, hooks, and contract utilities for Tangle Network apps",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "repository": {
@@ -0,0 +1,61 @@
1
+ /**
2
+ * OperatorOnboardingGuide — shared component for operator registration.
3
+ *
4
+ * Renders the registration command (cargo-tangle or cast) with a copy button.
5
+ * Each blueprint app (trading, sandbox, bazaar) used to reimplement this;
6
+ * now they install <OperatorOnboardingGuide> and pass their blueprint config.
7
+ */
8
+ import { type FC, useCallback, useState } from 'react';
9
+ import { Button } from './ui/button';
10
+ import {
11
+ useRegistrationCommand,
12
+ type RegistrationCommandOptions,
13
+ } from '../hooks/useRegistrationCommand';
14
+
15
+ export interface OperatorOnboardingGuideProps
16
+ extends RegistrationCommandOptions {
17
+ /** Optional title override. Defaults to "Register as operator". */
18
+ title?: string;
19
+ /** Optional description shown above the command. */
20
+ description?: string;
21
+ }
22
+
23
+ export const OperatorOnboardingGuide: FC<OperatorOnboardingGuideProps> = ({
24
+ title = 'Register as operator',
25
+ description = 'Run this command on your operator VPS to register for this blueprint on-chain.',
26
+ ...commandOptions
27
+ }) => {
28
+ const { command, label, blueprintIdNumber } =
29
+ useRegistrationCommand(commandOptions);
30
+ const [copied, setCopied] = useState(false);
31
+
32
+ const handleCopy = useCallback(() => {
33
+ navigator.clipboard.writeText(command).then(() => {
34
+ setCopied(true);
35
+ setTimeout(() => setCopied(false), 2000);
36
+ });
37
+ }, [command]);
38
+
39
+ return (
40
+ <div className="space-y-4">
41
+ <div>
42
+ <h3 className="text-lg font-semibold text-foreground">{title}</h3>
43
+ <p className="text-sm text-muted-foreground mt-1">{description}</p>
44
+ </div>
45
+
46
+ <div className="rounded-lg border border-border bg-muted/30 p-4">
47
+ <div className="flex items-center justify-between mb-2">
48
+ <span className="text-xs font-mono text-muted-foreground uppercase tracking-wider">
49
+ Blueprint #{blueprintIdNumber} · {label}
50
+ </span>
51
+ <Button size="sm" variant="outline" onClick={handleCopy}>
52
+ {copied ? 'Copied!' : 'Copy'}
53
+ </Button>
54
+ </div>
55
+ <pre className="text-sm font-mono text-foreground overflow-x-auto whitespace-pre-wrap break-all">
56
+ {command}
57
+ </pre>
58
+ </div>
59
+ </div>
60
+ );
61
+ };
package/src/components.ts CHANGED
@@ -55,3 +55,11 @@ export { JobExecutionDialog } from './components/forms/JobExecutionDialog';
55
55
 
56
56
  // ── Blueprint Host ──
57
57
  export { BlueprintHostHero, BlueprintHostPanel } from './host';
58
+
59
+ // ── Blueprint flows ──
60
+ export { OperatorOnboardingGuide } from './components/OperatorOnboardingGuide';
61
+ export type { OperatorOnboardingGuideProps } from './components/OperatorOnboardingGuide';
62
+
63
+ // ── Blueprint flows ──
64
+ export { OperatorOnboardingGuide } from './components/OperatorOnboardingGuide';
65
+ export type { OperatorOnboardingGuideProps } from './components/OperatorOnboardingGuide';
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Builds the operator registration command for a Tangle blueprint.
3
+ *
4
+ * Each blueprint app (trading arena, agent-sandbox, bazaar) used to reimplement
5
+ * this — encoding the `registerOperator` call, building the CLI command, and
6
+ * polling readiness. Lifted here so they install one hook instead of forking.
7
+ */
8
+ import { useMemo } from 'react';
9
+
10
+ export type RegistrationMode = 'cargo-tangle' | 'cast';
11
+
12
+ export interface RegistrationCommandOptions {
13
+ blueprintId: bigint;
14
+ rpcAddress: string;
15
+ ecdsaPublicKey: string;
16
+ rpcUrl: string;
17
+ registrationInputs?: string;
18
+ mode?: RegistrationMode;
19
+ /** Contract address for cast mode (defaults to TANGLE_CORE placeholder). */
20
+ servicesAddress?: string;
21
+ }
22
+
23
+ export interface RegistrationCommandResult {
24
+ /** The copy-paste command for the operator to run on their VPS. */
25
+ command: string;
26
+ /** A human-readable label for the command type. */
27
+ label: string;
28
+ /** The blueprint id as a plain number (for display). */
29
+ blueprintIdNumber: number;
30
+ }
31
+
32
+ /**
33
+ * Builds the operator registration command string for the given blueprint.
34
+ * Two modes:
35
+ * - `cargo-tangle` (default) — the canonical `cargo tangle blueprint register` flow.
36
+ * - `cast` — raw `cast send` for operators who manage their own keys.
37
+ */
38
+ export function useRegistrationCommand(
39
+ options: RegistrationCommandOptions,
40
+ ): RegistrationCommandResult {
41
+ const {
42
+ blueprintId,
43
+ rpcAddress,
44
+ ecdsaPublicKey,
45
+ rpcUrl,
46
+ registrationInputs = '0x',
47
+ mode = 'cargo-tangle',
48
+ servicesAddress = 'TANGLE_CORE',
49
+ } = options;
50
+
51
+ const blueprintIdNumber = Number(blueprintId);
52
+
53
+ const command = useMemo(() => {
54
+ if (mode === 'cast') {
55
+ return [
56
+ `cast send ${servicesAddress} \\`,
57
+ ` "registerOperator(uint64,bytes,string,bytes)" \\`,
58
+ ` ${blueprintIdNumber} \\`,
59
+ ` ${ecdsaPublicKey} \\`,
60
+ ` ${rpcAddress} \\`,
61
+ ` ${registrationInputs} \\`,
62
+ ` --rpc-url ${rpcUrl} \\`,
63
+ ` --private-key <YOUR_OPERATOR_KEY>`,
64
+ ].join('\n');
65
+ }
66
+
67
+ const wsUrl = rpcUrl.replace(/^http/, 'ws');
68
+
69
+ return [
70
+ `cargo tangle blueprint register \\`,
71
+ ` --blueprint-id ${blueprintIdNumber} \\`,
72
+ ` --http-rpc-url ${rpcUrl} \\`,
73
+ ` --ws-rpc-url ${wsUrl} \\`,
74
+ ` --keystore-uri <YOUR_KEYSTORE> \\`,
75
+ ` --rpc-address ${rpcAddress} \\`,
76
+ ` --ecdsa-public-key ${ecdsaPublicKey}`,
77
+ ].join('\n');
78
+ }, [blueprintIdNumber, ecdsaPublicKey, mode, registrationInputs, rpcAddress, rpcUrl, servicesAddress]);
79
+
80
+ const label = mode === 'cast' ? 'cast send (raw)' : 'cargo-tangle (canonical)';
81
+
82
+ return { command, label, blueprintIdNumber };
83
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Hook to request a service instance on-chain.
3
+ * Thin wrapper around wagmi's useWriteContract + the existing tangleServicesAbi.
4
+ */
5
+ import { useWriteContract } from 'wagmi';
6
+ import { tangleServicesAbi } from '../contracts/abi';
7
+ import { getAddresses } from '../contracts/publicClient';
8
+ import type { Address } from 'viem';
9
+ import { useCallback } from 'react';
10
+
11
+ export interface ServiceRequestParams {
12
+ blueprintId: bigint;
13
+ operators: Address[];
14
+ config: `0x${string}`;
15
+ permittedCallers: Address[];
16
+ ttl: bigint;
17
+ paymentToken: Address;
18
+ paymentAmount: bigint;
19
+ }
20
+
21
+ export function useServiceRequest(): {
22
+ requestService: (params: ServiceRequestParams) => Promise<`0x${string}`>;
23
+ isPending: boolean;
24
+ error: Error | null;
25
+ txHash: `0x${string}` | undefined;
26
+ } {
27
+ const { writeContractAsync, isPending, error, data: txHash } =
28
+ useWriteContract();
29
+
30
+ const requestService = useCallback(
31
+ async (params: ServiceRequestParams) => {
32
+ const addresses = getAddresses();
33
+ return writeContractAsync({
34
+ address: addresses.services,
35
+ abi: tangleServicesAbi,
36
+ functionName: 'requestService',
37
+ args: [
38
+ params.blueprintId,
39
+ params.operators,
40
+ params.config,
41
+ params.permittedCallers,
42
+ params.ttl,
43
+ params.paymentToken,
44
+ params.paymentAmount,
45
+ ],
46
+ });
47
+ },
48
+ [writeContractAsync],
49
+ );
50
+
51
+ return { requestService, isPending, error, txHash };
52
+ }
package/src/index.ts CHANGED
@@ -123,3 +123,19 @@ export type { UseSidecarAuthOptions, SidecarAuth } from './hooks/useSidecarAuth'
123
123
  export { useSidecarAuth } from './hooks/useSidecarAuth';
124
124
  export { useWagmiSidecarAuth } from './hooks/useWagmiSidecarAuth';
125
125
  export { useWalletEthBalance } from './hooks/useWalletEthBalance';
126
+
127
+ // ── Registration ──
128
+ export { useRegistrationCommand } from './hooks/useRegistrationCommand';
129
+ export type { RegistrationCommandOptions, RegistrationCommandResult, RegistrationMode } from './hooks/useRegistrationCommand';
130
+
131
+ // ── Service Request ──
132
+ export { useServiceRequest } from './hooks/useServiceRequest';
133
+ export type { ServiceRequestParams } from './hooks/useServiceRequest';
134
+
135
+ // ── Registration ──
136
+ export { useRegistrationCommand } from './hooks/useRegistrationCommand';
137
+ export type { RegistrationCommandOptions, RegistrationCommandResult, RegistrationMode } from './hooks/useRegistrationCommand';
138
+
139
+ // ── Service Request ──
140
+ export { useServiceRequest } from './hooks/useServiceRequest';
141
+ export type { ServiceRequestParams } from './hooks/useServiceRequest';
@@ -1,124 +0,0 @@
1
- import { Address } from 'viem';
2
- import * as react_jsx_runtime from 'react/jsx-runtime';
3
- import * as React from 'react';
4
- import * as class_variance_authority_types from 'class-variance-authority/types';
5
- import { VariantProps } from 'class-variance-authority';
6
-
7
- /**
8
- * Blueprint Registry — defines the metadata layer for Tangle blueprints.
9
- *
10
- * Each blueprint exposes a set of jobs. The registry maps on-chain job IDs
11
- * to human-readable metadata: labels, descriptions, categories, form fields,
12
- * and pricing info. This enables the UI to render appropriate forms for each
13
- * job without procedurally generating UI from raw ABI data.
14
- *
15
- * Third-party blueprints can register here to appear in the wizard.
16
- */
17
- type JobCategory = 'lifecycle' | 'execution' | 'batch' | 'workflow' | 'ssh' | 'management';
18
- interface JobFieldDef {
19
- name: string;
20
- label: string;
21
- type: 'text' | 'textarea' | 'number' | 'boolean' | 'select' | 'json' | 'combobox';
22
- placeholder?: string;
23
- required?: boolean;
24
- defaultValue?: string | number | boolean;
25
- options?: {
26
- label: string;
27
- value: string;
28
- }[];
29
- helperText?: string;
30
- /** Minimum allowed value for number fields */
31
- min?: number;
32
- /** Maximum allowed value for number fields */
33
- max?: number;
34
- /** Step increment for number fields */
35
- step?: number;
36
- /** Solidity ABI type for encoding (e.g. 'string', 'uint64', 'bool', 'uint8') */
37
- abiType?: string;
38
- /** ABI param name if different from `name` (e.g. 'agent_identifier' vs 'agentIdentifier') */
39
- abiParam?: string;
40
- /** Field is included in ABI encoding but never shown in form (e.g. sidecar_token) */
41
- internal?: boolean;
42
- }
43
- /** ABI param injected from runtime context, not user input (e.g. sidecar_url, sandbox_id) */
44
- interface AbiContextParam {
45
- abiName: string;
46
- abiType: string;
47
- }
48
- interface JobDefinition {
49
- id: number;
50
- name: string;
51
- label: string;
52
- description: string;
53
- category: JobCategory;
54
- icon: string;
55
- pricingMultiplier: number;
56
- /** Fields the user needs to fill for this job */
57
- fields: JobFieldDef[];
58
- /** Whether this job requires an existing sandbox to target */
59
- requiresSandbox: boolean;
60
- /** Optional warning shown before submission */
61
- warning?: string;
62
- /** ABI params prepended from runtime context (e.g. sidecar_url for sandbox jobs) */
63
- contextParams?: AbiContextParam[];
64
- /** Override for jobs with complex ABI encoding (e.g. nested structs) */
65
- customEncoder?: (values: Record<string, unknown>, context?: Record<string, unknown>) => `0x${string}`;
66
- }
67
- interface BlueprintDefinition {
68
- id: string;
69
- name: string;
70
- version: string;
71
- description: string;
72
- icon: string;
73
- color: string;
74
- /** Contract address per chain ID — resolved at runtime */
75
- contracts: Record<number, Address>;
76
- /** Supported job definitions */
77
- jobs: JobDefinition[];
78
- /** Category ordering for the UI */
79
- categories: {
80
- key: JobCategory;
81
- label: string;
82
- icon: string;
83
- }[];
84
- }
85
- declare function registerBlueprint(bp: BlueprintDefinition): void;
86
- declare function getBlueprint(id: string): BlueprintDefinition | undefined;
87
- declare function getAllBlueprints(): BlueprintDefinition[];
88
- declare function getBlueprintJobs(blueprintId: string, category?: JobCategory): JobDefinition[];
89
- declare function getJobById(blueprintId: string, jobId: number): JobDefinition | undefined;
90
-
91
- declare const buttonVariants: (props?: ({
92
- variant?: "default" | "link" | "success" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
93
- size?: "default" | "sm" | "lg" | "icon" | "icon-sm" | null | undefined;
94
- } & class_variance_authority_types.ClassProp) | undefined) => string;
95
- declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<'button'> & VariantProps<typeof buttonVariants> & {
96
- asChild?: boolean;
97
- }): react_jsx_runtime.JSX.Element;
98
-
99
- type Action = {
100
- label: string;
101
- href?: string;
102
- onClick?: () => void;
103
- variant?: React.ComponentProps<typeof Button>['variant'];
104
- disabled?: boolean;
105
- };
106
- type BlueprintHostHeroProps = {
107
- title: string;
108
- tagline?: string;
109
- description?: string;
110
- badges?: string[];
111
- actions?: Action[];
112
- children?: React.ReactNode;
113
- className?: string;
114
- };
115
- declare function BlueprintHostHero({ title, tagline, description, badges, actions, children, className, }: BlueprintHostHeroProps): react_jsx_runtime.JSX.Element;
116
-
117
- type BlueprintHostPanelProps = {
118
- title: string;
119
- children: React.ReactNode;
120
- className?: string;
121
- };
122
- declare function BlueprintHostPanel({ title, children, className, }: BlueprintHostPanelProps): react_jsx_runtime.JSX.Element;
123
-
124
- export { type AbiContextParam as A, type BlueprintDefinition as B, type JobDefinition as J, BlueprintHostHero as a, type BlueprintHostHeroProps as b, BlueprintHostPanel as c, type BlueprintHostPanelProps as d, type JobCategory as e, type JobFieldDef as f, getAllBlueprints as g, getBlueprint as h, getBlueprintJobs as i, getJobById as j, Button as k, buttonVariants as l, registerBlueprint as r };
@@ -1,55 +0,0 @@
1
- // src/preset.ts
2
- function bpThemeTokens(prefix) {
3
- return {
4
- elements: {
5
- borderColor: `var(--${prefix}-elements-borderColor)`,
6
- borderColorActive: `var(--${prefix}-elements-borderColorActive)`,
7
- background: {
8
- depth: {
9
- 1: `var(--${prefix}-elements-bg-depth-1)`,
10
- 2: `var(--${prefix}-elements-bg-depth-2)`,
11
- 3: `var(--${prefix}-elements-bg-depth-3)`,
12
- 4: `var(--${prefix}-elements-bg-depth-4)`
13
- }
14
- },
15
- textPrimary: `var(--${prefix}-elements-textPrimary)`,
16
- textSecondary: `var(--${prefix}-elements-textSecondary)`,
17
- textTertiary: `var(--${prefix}-elements-textTertiary)`,
18
- button: {
19
- primary: {
20
- background: `var(--${prefix}-elements-button-primary-background)`,
21
- backgroundHover: `var(--${prefix}-elements-button-primary-backgroundHover)`,
22
- text: `var(--${prefix}-elements-button-primary-text)`
23
- },
24
- secondary: {
25
- background: `var(--${prefix}-elements-button-secondary-background)`,
26
- backgroundHover: `var(--${prefix}-elements-button-secondary-backgroundHover)`,
27
- text: `var(--${prefix}-elements-button-secondary-text)`
28
- },
29
- danger: {
30
- background: `var(--${prefix}-elements-button-danger-background)`,
31
- backgroundHover: `var(--${prefix}-elements-button-danger-backgroundHover)`,
32
- text: `var(--${prefix}-elements-button-danger-text)`
33
- }
34
- },
35
- icon: {
36
- success: `var(--${prefix}-elements-icon-success)`,
37
- error: `var(--${prefix}-elements-icon-error)`,
38
- warning: `var(--${prefix}-elements-icon-warning)`,
39
- primary: `var(--${prefix}-elements-icon-primary)`,
40
- secondary: `var(--${prefix}-elements-icon-secondary)`
41
- },
42
- dividerColor: `var(--${prefix}-elements-dividerColor)`,
43
- item: {
44
- backgroundHover: `var(--${prefix}-elements-item-backgroundHover)`,
45
- backgroundActive: `var(--${prefix}-elements-item-backgroundActive)`
46
- },
47
- focus: `var(--${prefix}-elements-focus)`
48
- }
49
- };
50
- }
51
-
52
- export {
53
- bpThemeTokens
54
- };
55
- //# sourceMappingURL=chunk-37ADATBT.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/preset.ts"],"sourcesContent":["/**\n * Blueprint UI semantic token theme for UnoCSS.\n *\n * Components in this package use classes like `text-bp-elements-textPrimary`.\n * Each consuming app maps these to their own CSS variables by adding this\n * theme object under a `bp` key in their UnoCSS config:\n *\n * import { bpThemeTokens } from '@tangle-network/blueprint-ui/preset';\n * // theme: { colors: { bp: bpThemeTokens('cloud') } }\n * // theme: { colors: { bp: bpThemeTokens('arena') } }\n */\n\nexport function bpThemeTokens(prefix: string) {\n return {\n elements: {\n borderColor: `var(--${prefix}-elements-borderColor)`,\n borderColorActive: `var(--${prefix}-elements-borderColorActive)`,\n background: {\n depth: {\n 1: `var(--${prefix}-elements-bg-depth-1)`,\n 2: `var(--${prefix}-elements-bg-depth-2)`,\n 3: `var(--${prefix}-elements-bg-depth-3)`,\n 4: `var(--${prefix}-elements-bg-depth-4)`,\n },\n },\n textPrimary: `var(--${prefix}-elements-textPrimary)`,\n textSecondary: `var(--${prefix}-elements-textSecondary)`,\n textTertiary: `var(--${prefix}-elements-textTertiary)`,\n button: {\n primary: {\n background: `var(--${prefix}-elements-button-primary-background)`,\n backgroundHover: `var(--${prefix}-elements-button-primary-backgroundHover)`,\n text: `var(--${prefix}-elements-button-primary-text)`,\n },\n secondary: {\n background: `var(--${prefix}-elements-button-secondary-background)`,\n backgroundHover: `var(--${prefix}-elements-button-secondary-backgroundHover)`,\n text: `var(--${prefix}-elements-button-secondary-text)`,\n },\n danger: {\n background: `var(--${prefix}-elements-button-danger-background)`,\n backgroundHover: `var(--${prefix}-elements-button-danger-backgroundHover)`,\n text: `var(--${prefix}-elements-button-danger-text)`,\n },\n },\n icon: {\n success: `var(--${prefix}-elements-icon-success)`,\n error: `var(--${prefix}-elements-icon-error)`,\n warning: `var(--${prefix}-elements-icon-warning)`,\n primary: `var(--${prefix}-elements-icon-primary)`,\n secondary: `var(--${prefix}-elements-icon-secondary)`,\n },\n dividerColor: `var(--${prefix}-elements-dividerColor)`,\n item: {\n backgroundHover: `var(--${prefix}-elements-item-backgroundHover)`,\n backgroundActive: `var(--${prefix}-elements-item-backgroundActive)`,\n },\n focus: `var(--${prefix}-elements-focus)`,\n },\n };\n}\n"],"mappings":";AAYO,SAAS,cAAc,QAAgB;AAC5C,SAAO;AAAA,IACL,UAAU;AAAA,MACR,aAAa,SAAS,MAAM;AAAA,MAC5B,mBAAmB,SAAS,MAAM;AAAA,MAClC,YAAY;AAAA,QACV,OAAO;AAAA,UACL,GAAG,SAAS,MAAM;AAAA,UAClB,GAAG,SAAS,MAAM;AAAA,UAClB,GAAG,SAAS,MAAM;AAAA,UAClB,GAAG,SAAS,MAAM;AAAA,QACpB;AAAA,MACF;AAAA,MACA,aAAa,SAAS,MAAM;AAAA,MAC5B,eAAe,SAAS,MAAM;AAAA,MAC9B,cAAc,SAAS,MAAM;AAAA,MAC7B,QAAQ;AAAA,QACN,SAAS;AAAA,UACP,YAAY,SAAS,MAAM;AAAA,UAC3B,iBAAiB,SAAS,MAAM;AAAA,UAChC,MAAM,SAAS,MAAM;AAAA,QACvB;AAAA,QACA,WAAW;AAAA,UACT,YAAY,SAAS,MAAM;AAAA,UAC3B,iBAAiB,SAAS,MAAM;AAAA,UAChC,MAAM,SAAS,MAAM;AAAA,QACvB;AAAA,QACA,QAAQ;AAAA,UACN,YAAY,SAAS,MAAM;AAAA,UAC3B,iBAAiB,SAAS,MAAM;AAAA,UAChC,MAAM,SAAS,MAAM;AAAA,QACvB;AAAA,MACF;AAAA,MACA,MAAM;AAAA,QACJ,SAAS,SAAS,MAAM;AAAA,QACxB,OAAO,SAAS,MAAM;AAAA,QACtB,SAAS,SAAS,MAAM;AAAA,QACxB,SAAS,SAAS,MAAM;AAAA,QACxB,WAAW,SAAS,MAAM;AAAA,MAC5B;AAAA,MACA,cAAc,SAAS,MAAM;AAAA,MAC7B,MAAM;AAAA,QACJ,iBAAiB,SAAS,MAAM;AAAA,QAChC,kBAAkB,SAAS,MAAM;AAAA,MACnC;AAAA,MACA,OAAO,SAAS,MAAM;AAAA,IACxB;AAAA,EACF;AACF;","names":[]}