@tangle-network/blueprint-ui 0.1.1 → 0.1.2
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/README.md +38 -4
- package/dist/BlueprintHostPanel-6iVEh-f1.d.ts +39 -0
- package/dist/chunk-37ADATBT.js +55 -0
- package/dist/chunk-37ADATBT.js.map +1 -0
- package/dist/chunk-A6PJT5YQ.js +1180 -0
- package/dist/chunk-A6PJT5YQ.js.map +1 -0
- package/dist/chunk-GD3AZEJL.js +327 -0
- package/dist/chunk-GD3AZEJL.js.map +1 -0
- package/dist/components.d.ts +179 -0
- package/dist/components.js +1127 -0
- package/dist/components.js.map +1 -0
- package/dist/host.d.ts +96 -0
- package/dist/host.js +39 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +8470 -0
- package/dist/index.js +841 -0
- package/dist/index.js.map +1 -0
- package/dist/preset.d.ts +60 -0
- package/dist/preset.js +7 -0
- package/dist/preset.js.map +1 -0
- package/dist/registry-JhwB9BPD.d.ts +87 -0
- package/dist/styles.css +559 -0
- package/package.json +42 -13
- package/src/components.ts +3 -0
- package/src/contracts/abi.ts +9 -0
- package/src/contracts/chains.ts +4 -3
- package/src/contracts/publicClient.ts +2 -1
- package/src/hooks/useProvisionProgress.ts +2 -1
- package/src/hooks/useQuotes.ts +69 -14
- package/src/hooks/useSessionAuth.ts +2 -1
- package/src/host/components/BlueprintHostHero.tsx +91 -0
- package/src/host/components/BlueprintHostPanel.tsx +24 -0
- package/src/host/index.ts +42 -0
- package/src/host/resolver.ts +204 -0
- package/src/host/types.ts +111 -0
- package/src/index.ts +41 -1
- package/src/stores/infra.ts +3 -2
- package/src/styles.css +128 -0
- package/src/utils/env.ts +22 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/blueprint-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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": {
|
|
@@ -12,9 +12,13 @@
|
|
|
12
12
|
"url": "https://github.com/tangle-network/blueprint-ui/issues"
|
|
13
13
|
},
|
|
14
14
|
"type": "module",
|
|
15
|
-
"main": "./
|
|
16
|
-
"types": "./
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"sideEffects": [
|
|
18
|
+
"**/*.css"
|
|
19
|
+
],
|
|
17
20
|
"files": [
|
|
21
|
+
"dist",
|
|
18
22
|
"src",
|
|
19
23
|
"README.md",
|
|
20
24
|
"package.json",
|
|
@@ -24,16 +28,37 @@
|
|
|
24
28
|
"access": "public"
|
|
25
29
|
},
|
|
26
30
|
"exports": {
|
|
27
|
-
".":
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
".": {
|
|
32
|
+
"import": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./host": {
|
|
37
|
+
"import": "./dist/host.js",
|
|
38
|
+
"types": "./dist/host.d.ts",
|
|
39
|
+
"default": "./dist/host.js"
|
|
40
|
+
},
|
|
41
|
+
"./components": {
|
|
42
|
+
"import": "./dist/components.js",
|
|
43
|
+
"types": "./dist/components.d.ts",
|
|
44
|
+
"default": "./dist/components.js"
|
|
45
|
+
},
|
|
46
|
+
"./preset": {
|
|
47
|
+
"import": "./dist/preset.js",
|
|
48
|
+
"types": "./dist/preset.d.ts",
|
|
49
|
+
"default": "./dist/preset.js"
|
|
50
|
+
},
|
|
51
|
+
"./styles.css": "./dist/styles.css"
|
|
30
52
|
},
|
|
31
53
|
"scripts": {
|
|
32
|
-
"
|
|
54
|
+
"build": "tsup && node scripts/build-styles.mjs",
|
|
55
|
+
"dev": "tsup --watch",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"prepack": "npm run build"
|
|
33
58
|
},
|
|
34
59
|
"peerDependencies": {
|
|
35
60
|
"@tanstack/react-query": "^5.0.0",
|
|
36
|
-
"@nanostores/react": "^0.7.0",
|
|
61
|
+
"@nanostores/react": "^0.7.0 || ^1.0.0",
|
|
37
62
|
"@radix-ui/react-dialog": "^1.1.0",
|
|
38
63
|
"@radix-ui/react-select": "^2.1.0",
|
|
39
64
|
"@radix-ui/react-separator": "^1.1.0",
|
|
@@ -44,14 +69,14 @@
|
|
|
44
69
|
"class-variance-authority": "^0.7.0",
|
|
45
70
|
"clsx": "^2.1.0",
|
|
46
71
|
"framer-motion": "^12.0.0",
|
|
47
|
-
"nanostores": "^0.10.0",
|
|
48
|
-
"react": "^19.0.0",
|
|
49
|
-
"react-dom": "^19.0.0",
|
|
72
|
+
"nanostores": "^0.10.0 || ^1.0.0",
|
|
73
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
74
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
50
75
|
"react-router": "^7.0.0",
|
|
51
76
|
"sonner": "^2.0.0",
|
|
52
|
-
"tailwind-merge": "^3.2.0",
|
|
77
|
+
"tailwind-merge": "^2.6.0 || ^3.2.0",
|
|
53
78
|
"viem": "^2.31.0",
|
|
54
|
-
"wagmi": "^3.3.0"
|
|
79
|
+
"wagmi": "^2.19.0 || ^3.3.0"
|
|
55
80
|
},
|
|
56
81
|
"peerDependenciesMeta": {},
|
|
57
82
|
"devDependencies": {
|
|
@@ -65,6 +90,7 @@
|
|
|
65
90
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
66
91
|
"@types/react": "18.3.1",
|
|
67
92
|
"@types/react-dom": "18.3.1",
|
|
93
|
+
"@iconify-json/ph": "^1.2.2",
|
|
68
94
|
"blo": "^2.0.0",
|
|
69
95
|
"class-variance-authority": "^0.7.1",
|
|
70
96
|
"clsx": "^2.1.1",
|
|
@@ -75,7 +101,10 @@
|
|
|
75
101
|
"react-router": "^7.13.0",
|
|
76
102
|
"sonner": "^2.0.7",
|
|
77
103
|
"tailwind-merge": "^3.5.0",
|
|
104
|
+
"tsup": "^8.5.0",
|
|
78
105
|
"typescript": "^5.5.2",
|
|
106
|
+
"unocss": "^66.5.4",
|
|
107
|
+
"unocss-preset-animations": "^1.1.1",
|
|
79
108
|
"viem": "^2.46.2",
|
|
80
109
|
"wagmi": "^3.5.0"
|
|
81
110
|
}
|
package/src/components.ts
CHANGED
|
@@ -52,3 +52,6 @@ export type { FormSection } from './components/forms/BlueprintJobForm';
|
|
|
52
52
|
export { BlueprintJobForm } from './components/forms/BlueprintJobForm';
|
|
53
53
|
export { FormSummary } from './components/forms/FormSummary';
|
|
54
54
|
export { JobExecutionDialog } from './components/forms/JobExecutionDialog';
|
|
55
|
+
|
|
56
|
+
// ── Blueprint Host ──
|
|
57
|
+
export { BlueprintHostHero, BlueprintHostPanel } from './host';
|
package/src/contracts/abi.ts
CHANGED
|
@@ -103,6 +103,7 @@ export const tangleServicesAbi = [
|
|
|
103
103
|
{ name: 'totalCost', type: 'uint256' },
|
|
104
104
|
{ name: 'timestamp', type: 'uint64' },
|
|
105
105
|
{ name: 'expiry', type: 'uint64' },
|
|
106
|
+
{ name: 'confidentiality', type: 'uint8' },
|
|
106
107
|
{
|
|
107
108
|
name: 'securityCommitments',
|
|
108
109
|
type: 'tuple[]',
|
|
@@ -118,6 +119,14 @@ export const tangleServicesAbi = [
|
|
|
118
119
|
{ name: 'exposureBps', type: 'uint16' },
|
|
119
120
|
],
|
|
120
121
|
},
|
|
122
|
+
{
|
|
123
|
+
name: 'resourceCommitments',
|
|
124
|
+
type: 'tuple[]',
|
|
125
|
+
components: [
|
|
126
|
+
{ name: 'kind', type: 'uint8' },
|
|
127
|
+
{ name: 'count', type: 'uint64' },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
121
130
|
],
|
|
122
131
|
},
|
|
123
132
|
{ name: 'signature', type: 'bytes' },
|
package/src/contracts/chains.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { defineChain } from 'viem';
|
|
2
2
|
import { mainnet } from 'viem/chains';
|
|
3
3
|
import type { Address, Chain } from 'viem';
|
|
4
|
+
import { getEnvVar, isDevEnv } from '../utils/env';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Resolve RPC URL for the current environment.
|
|
7
8
|
* Handles local dev (hostname swap), Vite dev proxy, and remote access.
|
|
8
9
|
*/
|
|
9
10
|
export function resolveRpcUrl(envUrl?: string): string {
|
|
10
|
-
const configured = envUrl ??
|
|
11
|
+
const configured = envUrl ?? getEnvVar('VITE_RPC_URL') ?? 'http://localhost:8545';
|
|
11
12
|
if (typeof window === 'undefined') return configured;
|
|
12
13
|
try {
|
|
13
14
|
const rpc = new URL(configured);
|
|
@@ -15,7 +16,7 @@ export function resolveRpcUrl(envUrl?: string): string {
|
|
|
15
16
|
const pageHost = window.location.hostname;
|
|
16
17
|
const isLocalPage = pageHost === '127.0.0.1' || pageHost === 'localhost';
|
|
17
18
|
// Dev-mode proxy for LAN access to local RPC
|
|
18
|
-
if (isLocalRpc && !isLocalPage &&
|
|
19
|
+
if (isLocalRpc && !isLocalPage && isDevEnv()) {
|
|
19
20
|
return `${window.location.origin}/rpc-proxy`;
|
|
20
21
|
}
|
|
21
22
|
// Non-dev LAN access: swap hostname
|
|
@@ -37,7 +38,7 @@ export interface LocalChainOptions {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export function createTangleLocalChain(options: LocalChainOptions = {}) {
|
|
40
|
-
const chainId = options.chainId ?? Number(
|
|
41
|
+
const chainId = options.chainId ?? Number(getEnvVar('VITE_CHAIN_ID') ?? 31337);
|
|
41
42
|
const localRpcUrl = resolveRpcUrl(options.rpcUrl);
|
|
42
43
|
|
|
43
44
|
return defineChain({
|
|
@@ -3,8 +3,9 @@ import type { PublicClient } from 'viem';
|
|
|
3
3
|
import { atom } from 'nanostores';
|
|
4
4
|
import { getNetworks, tangleLocal, type CoreAddresses } from './chains';
|
|
5
5
|
import { persistedAtom } from '../stores/persistedAtom';
|
|
6
|
+
import { getEnvVar } from '../utils/env';
|
|
6
7
|
|
|
7
|
-
const defaultChainId = Number(
|
|
8
|
+
const defaultChainId = Number(getEnvVar('VITE_CHAIN_ID') ?? tangleLocal.id);
|
|
8
9
|
|
|
9
10
|
export const selectedChainIdStore = persistedAtom<number>({
|
|
10
11
|
key: 'bp_selected_chain',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { getEnvVar } from '../utils/env';
|
|
2
3
|
|
|
3
4
|
// ---------------------------------------------------------------------------
|
|
4
5
|
// Types matching sandbox-runtime/src/provision_progress.rs
|
|
@@ -64,7 +65,7 @@ export function useProvisionProgress({
|
|
|
64
65
|
const [isPolling, setIsPolling] = useState(false);
|
|
65
66
|
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
66
67
|
|
|
67
|
-
const baseUrl = apiUrl ??
|
|
68
|
+
const baseUrl = apiUrl ?? getEnvVar('VITE_OPERATOR_API_URL') ?? 'http://localhost:9090';
|
|
68
69
|
|
|
69
70
|
const fetchProgress = useCallback(async () => {
|
|
70
71
|
if (callId == null) return;
|
package/src/hooks/useQuotes.ts
CHANGED
|
@@ -18,6 +18,16 @@ import { resolveOperatorRpc } from '../utils/resolveOperatorRpc';
|
|
|
18
18
|
|
|
19
19
|
// ── Types ──
|
|
20
20
|
|
|
21
|
+
type SecurityCommitment = {
|
|
22
|
+
asset: { kind: number; token: Address };
|
|
23
|
+
exposureBps: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type ResourceCommitment = {
|
|
27
|
+
kind: number;
|
|
28
|
+
count: bigint;
|
|
29
|
+
};
|
|
30
|
+
|
|
21
31
|
export interface OperatorQuote {
|
|
22
32
|
operator: Address;
|
|
23
33
|
totalCost: bigint;
|
|
@@ -28,12 +38,13 @@ export interface OperatorQuote {
|
|
|
28
38
|
totalCost: bigint;
|
|
29
39
|
timestamp: bigint;
|
|
30
40
|
expiry: bigint;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}[];
|
|
41
|
+
confidentiality: number;
|
|
42
|
+
securityCommitments: readonly SecurityCommitment[];
|
|
43
|
+
resourceCommitments: readonly ResourceCommitment[];
|
|
35
44
|
};
|
|
36
45
|
costRate: number;
|
|
46
|
+
teeAttested?: boolean;
|
|
47
|
+
teeProvider?: string;
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
export interface UseQuotesResult {
|
|
@@ -49,6 +60,20 @@ export interface UseQuotesResult {
|
|
|
49
60
|
|
|
50
61
|
const POW_DIFFICULTY = 20;
|
|
51
62
|
const WEI_PER_TNT = 1_000_000_000_000_000_000; // 10^18
|
|
63
|
+
const RESOURCE_KIND_TO_ID = {
|
|
64
|
+
CPU: 0,
|
|
65
|
+
MemoryMB: 1,
|
|
66
|
+
StorageMB: 2,
|
|
67
|
+
NetworkEgressMB: 3,
|
|
68
|
+
NetworkIngressMB: 4,
|
|
69
|
+
GPU: 5,
|
|
70
|
+
} as const;
|
|
71
|
+
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' as Address;
|
|
72
|
+
const DEFAULT_RESOURCE_REQUIREMENTS = [
|
|
73
|
+
{ kind: 'CPU', count: 1 },
|
|
74
|
+
{ kind: 'MemoryMB', count: 1024 },
|
|
75
|
+
{ kind: 'StorageMB', count: 10240 },
|
|
76
|
+
] as const;
|
|
52
77
|
|
|
53
78
|
function sha256(data: Uint8Array): Uint8Array {
|
|
54
79
|
return viemSha256(data, 'bytes');
|
|
@@ -114,6 +139,35 @@ export function formatCost(totalCost: bigint): string {
|
|
|
114
139
|
return `${tnt.toLocaleString(undefined, { maximumFractionDigits: 2 })} TNT`;
|
|
115
140
|
}
|
|
116
141
|
|
|
142
|
+
function quoteConfidentiality(requireTee: boolean): number {
|
|
143
|
+
return requireTee ? 1 : 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function resourceKindToId(kind: string): number {
|
|
147
|
+
const mapped = RESOURCE_KIND_TO_ID[kind as keyof typeof RESOURCE_KIND_TO_ID];
|
|
148
|
+
if (mapped === undefined) {
|
|
149
|
+
throw new Error(`Unsupported resource kind in quote: ${kind}`);
|
|
150
|
+
}
|
|
151
|
+
return mapped;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function mapJsonSecurityCommitment(sc: any): SecurityCommitment {
|
|
155
|
+
return {
|
|
156
|
+
asset: {
|
|
157
|
+
kind: sc.asset?.kind ?? 0,
|
|
158
|
+
token: (sc.asset?.token ?? ZERO_ADDRESS) as Address,
|
|
159
|
+
},
|
|
160
|
+
exposureBps: sc.exposure_bps ?? 0,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function mapJsonResourceCommitment(resource: any): ResourceCommitment {
|
|
165
|
+
return {
|
|
166
|
+
kind: resourceKindToId(String(resource.kind ?? 'CPU')),
|
|
167
|
+
count: BigInt(resource.count ?? 0),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
117
171
|
// ── Hook ──
|
|
118
172
|
|
|
119
173
|
export function useQuotes(
|
|
@@ -121,6 +175,7 @@ export function useQuotes(
|
|
|
121
175
|
blueprintId: bigint,
|
|
122
176
|
ttlBlocks: bigint,
|
|
123
177
|
enabled: boolean,
|
|
178
|
+
requireTee = false,
|
|
124
179
|
): UseQuotesResult {
|
|
125
180
|
const [quotes, setQuotes] = useState<OperatorQuote[]>([]);
|
|
126
181
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -168,6 +223,7 @@ export function useQuotes(
|
|
|
168
223
|
ttlBlocks,
|
|
169
224
|
proofOfWork: proof,
|
|
170
225
|
challengeTimestamp: timestamp,
|
|
226
|
+
requireTee,
|
|
171
227
|
});
|
|
172
228
|
|
|
173
229
|
if (!response) throw new Error('No quote returned from operator');
|
|
@@ -194,7 +250,7 @@ export function useQuotes(
|
|
|
194
250
|
return () => {
|
|
195
251
|
cancelled = true;
|
|
196
252
|
};
|
|
197
|
-
}, [operators, blueprintId, ttlBlocks, enabled, fetchKey]);
|
|
253
|
+
}, [operators, blueprintId, ttlBlocks, enabled, fetchKey, requireTee]);
|
|
198
254
|
|
|
199
255
|
const totalCost = quotes.reduce((sum, q) => sum + q.totalCost, 0n);
|
|
200
256
|
|
|
@@ -213,6 +269,7 @@ async function fetchPriceFromOperator(
|
|
|
213
269
|
ttlBlocks: bigint;
|
|
214
270
|
proofOfWork: Uint8Array;
|
|
215
271
|
challengeTimestamp: bigint;
|
|
272
|
+
requireTee: boolean;
|
|
216
273
|
},
|
|
217
274
|
): Promise<OperatorQuote | null> {
|
|
218
275
|
// Try JSON endpoint (simpler, no protobuf dependency required)
|
|
@@ -225,11 +282,8 @@ async function fetchPriceFromOperator(
|
|
|
225
282
|
ttl_blocks: String(params.ttlBlocks),
|
|
226
283
|
proof_of_work: toHex(params.proofOfWork),
|
|
227
284
|
challenge_timestamp: String(params.challengeTimestamp),
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
{ kind: 'MemoryMB', count: 1024 },
|
|
231
|
-
{ kind: 'StorageMB', count: 10240 },
|
|
232
|
-
],
|
|
285
|
+
require_tee: params.requireTee,
|
|
286
|
+
resource_requirements: DEFAULT_RESOURCE_REQUIREMENTS,
|
|
233
287
|
}),
|
|
234
288
|
signal: AbortSignal.timeout(10_000),
|
|
235
289
|
});
|
|
@@ -242,16 +296,17 @@ async function fetchPriceFromOperator(
|
|
|
242
296
|
totalCost: BigInt(data.total_cost ?? '0'),
|
|
243
297
|
signature: (data.signature ?? '0x') as `0x${string}`,
|
|
244
298
|
costRate: Number(data.cost_rate ?? 0),
|
|
299
|
+
teeAttested: Boolean(data.tee_attested),
|
|
300
|
+
teeProvider: data.tee_provider || undefined,
|
|
245
301
|
details: {
|
|
246
302
|
blueprintId: BigInt(data.details?.blueprint_id ?? params.blueprintId),
|
|
247
303
|
ttlBlocks: BigInt(data.details?.ttl_blocks ?? params.ttlBlocks),
|
|
248
304
|
totalCost: BigInt(data.details?.total_cost ?? '0'),
|
|
249
305
|
timestamp: BigInt(data.details?.timestamp ?? params.challengeTimestamp),
|
|
250
306
|
expiry: BigInt(data.details?.expiry ?? '0'),
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
})),
|
|
307
|
+
confidentiality: Number(data.details?.confidentiality ?? quoteConfidentiality(params.requireTee)),
|
|
308
|
+
securityCommitments: (data.details?.security_commitments ?? []).map(mapJsonSecurityCommitment),
|
|
309
|
+
resourceCommitments: (data.details?.resources ?? []).map(mapJsonResourceCommitment),
|
|
255
310
|
},
|
|
256
311
|
};
|
|
257
312
|
} catch {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useState } from 'react';
|
|
2
2
|
import { useSignMessage } from 'wagmi';
|
|
3
3
|
import { getSession, setSession, removeSession, type SessionEntry } from '../stores/session';
|
|
4
|
+
import { getEnvVar } from '../utils/env';
|
|
4
5
|
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
6
7
|
// Types
|
|
@@ -28,7 +29,7 @@ interface UseSessionAuthOptions {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export function useSessionAuth({ sandboxId, apiUrl }: UseSessionAuthOptions) {
|
|
31
|
-
const baseUrl = apiUrl ??
|
|
32
|
+
const baseUrl = apiUrl ?? getEnvVar('VITE_OPERATOR_API_URL') ?? 'http://localhost:9090';
|
|
32
33
|
const { signMessageAsync } = useSignMessage();
|
|
33
34
|
|
|
34
35
|
const [isAuthenticating, setIsAuthenticating] = useState(false);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '../../components/ui/card';
|
|
3
|
+
import { Badge } from '../../components/ui/badge';
|
|
4
|
+
import { Button } from '../../components/ui/button';
|
|
5
|
+
import { cn } from '../../utils';
|
|
6
|
+
|
|
7
|
+
type Action = {
|
|
8
|
+
label: string;
|
|
9
|
+
href?: string;
|
|
10
|
+
onClick?: () => void;
|
|
11
|
+
variant?: React.ComponentProps<typeof Button>['variant'];
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type BlueprintHostHeroProps = {
|
|
16
|
+
title: string;
|
|
17
|
+
tagline?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
badges?: string[];
|
|
20
|
+
actions?: Action[];
|
|
21
|
+
children?: React.ReactNode;
|
|
22
|
+
className?: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function BlueprintHostHero({
|
|
26
|
+
title,
|
|
27
|
+
tagline,
|
|
28
|
+
description,
|
|
29
|
+
badges = [],
|
|
30
|
+
actions = [],
|
|
31
|
+
children,
|
|
32
|
+
className,
|
|
33
|
+
}: BlueprintHostHeroProps) {
|
|
34
|
+
return (
|
|
35
|
+
<Card className={cn('rounded-3xl border-bp-elements-borderColor/70 bg-bp-elements-background-depth-2', className)}>
|
|
36
|
+
<CardHeader className="space-y-4">
|
|
37
|
+
{badges.length > 0 ? (
|
|
38
|
+
<div className="flex flex-wrap gap-2">
|
|
39
|
+
{badges.map((badge) => (
|
|
40
|
+
<Badge key={badge} variant="secondary">
|
|
41
|
+
{badge}
|
|
42
|
+
</Badge>
|
|
43
|
+
))}
|
|
44
|
+
</div>
|
|
45
|
+
) : null}
|
|
46
|
+
<div className="space-y-2">
|
|
47
|
+
<CardTitle className="text-3xl">{title}</CardTitle>
|
|
48
|
+
{tagline ? (
|
|
49
|
+
<CardDescription className="text-base text-bp-elements-textPrimary/80">
|
|
50
|
+
{tagline}
|
|
51
|
+
</CardDescription>
|
|
52
|
+
) : null}
|
|
53
|
+
</div>
|
|
54
|
+
{description ? (
|
|
55
|
+
<p className="max-w-3xl text-sm leading-6 text-bp-elements-textSecondary">
|
|
56
|
+
{description}
|
|
57
|
+
</p>
|
|
58
|
+
) : null}
|
|
59
|
+
</CardHeader>
|
|
60
|
+
{(actions.length > 0 || children) && (
|
|
61
|
+
<CardContent className="space-y-4">
|
|
62
|
+
{actions.length > 0 ? (
|
|
63
|
+
<div className="flex flex-wrap gap-3">
|
|
64
|
+
{actions.map((action) => {
|
|
65
|
+
const button = (
|
|
66
|
+
<Button
|
|
67
|
+
key={action.label}
|
|
68
|
+
variant={action.variant ?? 'default'}
|
|
69
|
+
onClick={action.onClick}
|
|
70
|
+
disabled={action.disabled}
|
|
71
|
+
>
|
|
72
|
+
{action.label}
|
|
73
|
+
</Button>
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
return action.href ? (
|
|
77
|
+
<a key={action.label} href={action.href}>
|
|
78
|
+
{button}
|
|
79
|
+
</a>
|
|
80
|
+
) : (
|
|
81
|
+
button
|
|
82
|
+
);
|
|
83
|
+
})}
|
|
84
|
+
</div>
|
|
85
|
+
) : null}
|
|
86
|
+
{children}
|
|
87
|
+
</CardContent>
|
|
88
|
+
)}
|
|
89
|
+
</Card>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Card, CardContent, CardHeader, CardTitle } from '../../components/ui/card';
|
|
3
|
+
import { cn } from '../../utils';
|
|
4
|
+
|
|
5
|
+
export type BlueprintHostPanelProps = {
|
|
6
|
+
title: string;
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function BlueprintHostPanel({
|
|
12
|
+
title,
|
|
13
|
+
children,
|
|
14
|
+
className,
|
|
15
|
+
}: BlueprintHostPanelProps) {
|
|
16
|
+
return (
|
|
17
|
+
<Card className={cn('rounded-3xl border-bp-elements-borderColor/70 bg-bp-elements-background-depth-2', className)}>
|
|
18
|
+
<CardHeader>
|
|
19
|
+
<CardTitle className="text-xl">{title}</CardTitle>
|
|
20
|
+
</CardHeader>
|
|
21
|
+
<CardContent>{children}</CardContent>
|
|
22
|
+
</Card>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
BlueprintAppVisibility,
|
|
3
|
+
BlueprintPublisherVerification,
|
|
4
|
+
BlueprintExperienceTier,
|
|
5
|
+
BlueprintSlugPolicy,
|
|
6
|
+
BlueprintUiSurface,
|
|
7
|
+
BlueprintResourceRoute,
|
|
8
|
+
BlueprintPermissionScope,
|
|
9
|
+
BlueprintExternalAppMode,
|
|
10
|
+
BlueprintExternalAppTrust,
|
|
11
|
+
BlueprintPublisher,
|
|
12
|
+
BlueprintResourceModel,
|
|
13
|
+
BlueprintPermissionDescriptor,
|
|
14
|
+
BlueprintExternalAppConfig,
|
|
15
|
+
BlueprintUiManifest,
|
|
16
|
+
BlueprintAppModuleBinding,
|
|
17
|
+
BlueprintAppEntry,
|
|
18
|
+
BlueprintAppResolvedView,
|
|
19
|
+
} from './types';
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
buildCanonicalBlueprintSlug,
|
|
23
|
+
resolveBlueprintAppView,
|
|
24
|
+
toBlueprintAppEntry,
|
|
25
|
+
getBlueprintExperienceTierLabel,
|
|
26
|
+
getBlueprintSlugPolicyLabel,
|
|
27
|
+
getBlueprintSurfaceLabel,
|
|
28
|
+
getBlueprintPublisherVerificationLabel,
|
|
29
|
+
getExternalAppTrustLabel,
|
|
30
|
+
isVerifiedBlueprintPublisher,
|
|
31
|
+
canPublisherClaimSlug,
|
|
32
|
+
isTrustedExternalAppHost,
|
|
33
|
+
getBlueprintPath,
|
|
34
|
+
getBlueprintServicePath,
|
|
35
|
+
sanitizeBlueprintSlugPart,
|
|
36
|
+
deriveBlueprintRequestedSlug,
|
|
37
|
+
} from './resolver';
|
|
38
|
+
|
|
39
|
+
export type { BlueprintHostHeroProps } from './components/BlueprintHostHero';
|
|
40
|
+
export { BlueprintHostHero } from './components/BlueprintHostHero';
|
|
41
|
+
export type { BlueprintHostPanelProps } from './components/BlueprintHostPanel';
|
|
42
|
+
export { BlueprintHostPanel } from './components/BlueprintHostPanel';
|