Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion apps/tangle-cloud/src/blueprintApps/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,15 @@ export function buildBlueprintManifestFromMetadata(
});
manifest.externalApp = externalAppParsed.externalApp;

// Declarative tier is unlocked at the URI-bound trust level
// (`verified-uri`) or stronger. The stricter `productionReady` gate
// (IPFS + signed attestation) is kept for `metadataVerified` below,
// which controls iframe/externalApp embedding — that needs payload
// attestation, not just URI binding.
const allowDeclarativeTier =
manifestRoot !== null &&
blueprint.metadataVerification?.productionReady === true;
(blueprint.metadataVerification?.productionReady === true ||
blueprint.metadataVerification?.status === 'verified-uri');
const trustedExternalApp =
manifest.externalApp?.trust === 'trusted'
? manifest.externalApp
Expand Down
30 changes: 26 additions & 4 deletions apps/tangle-cloud/src/blueprintApps/registry.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
import { resolveBlueprintAppView } from './resolver';
import type { BlueprintAppEntry } from './types';

/**
* Per-network blueprintId → curated-entry slug bindings.
*
* The upstream `BlueprintAppEntry` type only carries a single `blueprintId`,
* but in practice one curated app (e.g. the sandbox) maps to multiple
* on-chain IDs across networks and across retries (Base Sepolia currently
* has the sandbox registered at ids 0, 1, and 2). This map is the single
* source of truth for those bindings — `getBlueprintAppByBlueprintId`
* consults it first.
*
* Update this when:
* - re-registering a curated app on a new network
* - landing a brand new curated entry that ships with a known id
*/
const CURATED_BLUEPRINT_ID_TO_SLUG: ReadonlyMap<bigint, string> = new Map([
// Base Sepolia (chainId 84532) — see deployments/base-sepolia/blueprints.tsv
[0n, 'sandbox'],
[1n, 'sandbox'],
[2n, 'sandbox'],
]);

const entries = [
{
slug: 'trading',
canonicalSlug: 'trading',
blueprintId: 1n,
publisher: {
label: 'Tangle Labs',
visibility: 'first-party',
Expand Down Expand Up @@ -198,9 +218,11 @@ export function getBlueprintAppByCanonicalSlug(
export function getBlueprintAppByBlueprintId(
blueprintId: bigint,
): BlueprintAppEntry | null {
for (const entry of blueprintAppEntries) {
if (entry.blueprintId === blueprintId) {
return entry;
const mappedSlug = CURATED_BLUEPRINT_ID_TO_SLUG.get(blueprintId);
if (mappedSlug) {
const mapped = blueprintAppRegistry.get(mappedSlug);
if (mapped) {
return mapped;
}
}

Expand Down
2 changes: 1 addition & 1 deletion apps/tangle-cloud/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default function Header({
return (
<header
className={twMerge(
'tangle-cloud-topbar fixed top-0 right-0 left-0 z-40 flex h-14 items-center justify-between gap-4 border-b border-border bg-card px-4 font-sans text-[13px] tracking-tight lg:left-16 lg:px-8',
'tangle-cloud-topbar fixed top-0 right-0 left-0 z-40 flex h-14 items-center justify-between gap-4 border-b border-border bg-[var(--bg-elevated)]/85 px-4 font-sans text-[13px] tracking-tight backdrop-blur-md lg:left-16 lg:px-8',
className,
)}
{...props}
Expand Down
4 changes: 2 additions & 2 deletions apps/tangle-cloud/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const Sidebar: FC<Props> = ({ isExpandedByDefault, onExpandedChange }) => {
<>
<aside
className={twMerge(
'group/sidebar fixed bottom-0 left-0 top-0 z-50 hidden shrink-0 border-border border-r bg-background transition-[width] duration-200 lg:flex lg:flex-col',
'group/sidebar fixed bottom-0 left-0 top-0 z-50 hidden shrink-0 border-border border-r bg-card transition-[width] duration-200 lg:flex lg:flex-col',
isDesktopExpanded ? 'w-64' : 'w-16',
)}
>
Expand Down Expand Up @@ -152,7 +152,7 @@ const Sidebar: FC<Props> = ({ isExpandedByDefault, onExpandedChange }) => {

{isMobileOpen && (
<div className="fixed inset-0 z-[70] bg-black/50 lg:hidden">
<aside className="fixed bottom-0 left-0 top-0 w-64 border-r border-border bg-background shadow-[var(--shadow-card)]">
<aside className="fixed bottom-0 left-0 top-0 w-64 border-r border-border bg-card shadow-[var(--shadow-card)]">
<SidebarBrand isExpanded />
<SidebarNav
items={SIDEBAR_ITEMS}
Expand Down
74 changes: 74 additions & 0 deletions apps/tangle-cloud/src/components/blueprints/categoryColor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import type { CSSProperties } from 'react';

/**
* Stable HSL hue per known blueprint category. Categories rendered to users
* (`Inference`, `Training`, `Agents`, `Data`, `Trading`, `Other`, plus the
* legacy `Blueprint` fallback) get a fixed color so the catalog reads as
* categorized rather than monochrome. Anything not in the map hashes its
* label into the [0, 360) range, deterministically.
*/
const CATEGORY_HUE: Record<string, number> = {
Inference: 210,
'AI Inference': 210,
Training: 160,
'AI Training': 160,
Agents: 265,
Data: 195,
Trading: 38,
Other: 240,
Blueprint: 240,
};

export const categoryHue = (category: string | null | undefined): number => {
if (!category) return CATEGORY_HUE.Other;
if (category in CATEGORY_HUE) return CATEGORY_HUE[category];

let hash = 0;
for (let i = 0; i < category.length; i += 1) {
hash = (hash * 31 + category.charCodeAt(i)) % 360;
}
return hash;
};

export type CategoryPalette = {
hue: number;
bg: string;
border: string;
text: string;
stripe: string;
swatch: string;
};

export const categoryPalette = (
category: string | null | undefined,
): CategoryPalette => {
const hue = categoryHue(category);
return {
hue,
bg: `hsl(${hue} 70% 50% / 0.14)`,
border: `hsl(${hue} 70% 62% / 0.34)`,
text: `hsl(${hue} 90% 78%)`,
stripe: `hsl(${hue} 70% 60% / 0.55)`,
swatch: `hsl(${hue} 60% 16%)`,
};
};

export const categoryBadgeStyle = (
category: string | null | undefined,
): CSSProperties => {
const palette = categoryPalette(category);
return {
backgroundColor: palette.bg,
borderColor: palette.border,
color: palette.text,
};
};

export const categoryStripeStyle = (
category: string | null | undefined,
): CSSProperties => {
const palette = categoryPalette(category);
return {
boxShadow: `inset 0 2px 0 ${palette.stripe}`,
};
};
21 changes: 13 additions & 8 deletions apps/tangle-cloud/src/pages/blueprints/BlueprintListing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ import { twMerge } from 'tailwind-merge';
import { PagePath } from '../../types';
import { BlueprintVisual } from '../../components/blueprints/BlueprintVisual';
import { formatBlueprintName } from '../../components/blueprints/blueprintVisualUtils';
import {
categoryBadgeStyle,
categoryStripeStyle,
} from '../../components/blueprints/categoryColor';

const PAGE_SIZE = 12;
const ALL_CATEGORIES = 'All categories';
Expand Down Expand Up @@ -259,10 +263,7 @@ const BlueprintListing: FC<Props> = ({

return (
<div className="space-y-5">
<Card
variant="sandbox"
className="catalog-controls border-border bg-card"
>
<Card variant="elevated" className="catalog-controls">
<CardContent className="space-y-4 p-4 md:p-5">
<div className="grid gap-4 xl:grid-cols-[minmax(360px,1fr)_auto_auto] xl:items-center">
<Input
Expand Down Expand Up @@ -479,9 +480,10 @@ const BlueprintCard = ({
variant="sandbox"
hover
className={twMerge(
'blueprint-card group relative min-h-[410px] overflow-hidden border-border bg-card shadow-[var(--shadow-card)]',
'blueprint-card group relative min-h-[410px] overflow-hidden',
isSelected && 'border-primary shadow-[var(--shadow-accent)]',
)}
style={categoryStripeStyle(category)}
>
<Link
to={blueprintHref}
Expand All @@ -493,10 +495,13 @@ const BlueprintCard = ({
<BlueprintVisual blueprint={blueprint} category={category} compact />

<div className="mt-4">
<p className="font-semibold text-[10px] text-muted-foreground uppercase tracking-[0.18em]">
<span
className="inline-flex items-center rounded-full border px-2.5 py-0.5 font-semibold text-[10px] uppercase tracking-[0.18em]"
style={categoryBadgeStyle(category)}
>
{category}
</p>
<h3 className="mt-1 line-clamp-1 font-display font-extrabold text-foreground text-xl tracking-tight">
</span>
<h3 className="mt-2 line-clamp-1 font-display font-extrabold text-foreground text-xl tracking-tight">
{displayName}
</h3>
</div>
Expand Down
38 changes: 25 additions & 13 deletions apps/tangle-cloud/src/pages/blueprints/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import { useResolvedBlueprintViewFromIndexedBlueprint } from '../../../blueprint
import { TangleDAppPagePath } from '../../../types';
import { BlueprintVisual } from '../../../components/blueprints/BlueprintVisual';
import { formatBlueprintName } from '../../../components/blueprints/blueprintVisualUtils';
import {
categoryBadgeStyle,
categoryStripeStyle,
} from '../../../components/blueprints/categoryColor';

const Page: FC = () => {
const id = useParamWithSchema('id', z.string().min(1));
Expand Down Expand Up @@ -152,7 +156,8 @@ const BlueprintDetailHero = ({
<section className="grid gap-5 xl:grid-cols-[minmax(0,1fr)_420px]">
<Card
variant="sandbox"
className="overflow-hidden border-border bg-card shadow-[var(--shadow-card)]"
className="overflow-hidden"
style={categoryStripeStyle(category)}
>
<CardContent className="grid gap-6 p-5 md:grid-cols-[300px_minmax(0,1fr)] md:p-6">
<BlueprintVisual
Expand All @@ -163,7 +168,12 @@ const BlueprintDetailHero = ({

<div className="flex min-w-0 flex-col">
<div className="flex flex-wrap gap-2">
<Badge variant="outline">{category}</Badge>
<span
className="inline-flex items-center rounded-full border px-2.5 py-0.5 font-semibold text-[10px] uppercase tracking-wider"
style={categoryBadgeStyle(category)}
>
{category}
</span>
<Badge variant={operatorCount > 0 ? 'success' : 'outline'} dot>
{operatorCount} operator{operatorCount === 1 ? '' : 's'}
</Badge>
Expand Down Expand Up @@ -233,7 +243,7 @@ const BlueprintDetailHero = ({
</CardContent>
</Card>

<Card variant="sandbox" className="border-border bg-card">
<Card variant="elevated">
<CardContent className="p-5 md:p-6">
<div className="flex items-center justify-between gap-3 border-border border-b pb-4">
<div>
Expand All @@ -257,7 +267,7 @@ const BlueprintDetailHero = ({
{metadataItems.map(([label, value]) => (
<div
key={label}
className="rounded-lg border border-border bg-muted/30 p-3"
className="rounded-lg border border-border bg-card p-3"
>
<dt className="font-semibold text-muted-foreground text-[10px] uppercase tracking-wider">
{label}
Expand All @@ -269,7 +279,13 @@ const BlueprintDetailHero = ({
))}
</dl>

<div className="mt-5 rounded-lg border border-border bg-muted/20 p-4">
<div
className="mt-5 rounded-lg border p-4"
style={{
backgroundColor: 'var(--accent-surface-soft)',
borderColor: 'var(--border-accent)',
}}
>
<h3 className="font-display font-bold text-foreground text-base">
Before you commit
</h3>
Expand All @@ -288,7 +304,7 @@ const BlueprintDetailHero = ({
};

const LaunchFact = ({ label, value }: { label: string; value: string }) => (
<div className="rounded-lg border border-border bg-muted/25 p-3">
<div className="rounded-lg border border-border bg-card p-3">
<p className="font-semibold text-muted-foreground text-[10px] uppercase tracking-wider">
{label}
</p>
Expand All @@ -302,11 +318,7 @@ const RegisteredOperatorsPanel = ({
operators: BlueprintOperator[];
}) => {
return (
<Card
id="operators"
variant="sandbox"
className="border-border bg-card shadow-[var(--shadow-card)]"
>
<Card id="operators" variant="default">
<CardContent className="p-6">
<div className="flex flex-col gap-3 border-border border-b pb-5 sm:flex-row sm:items-end sm:justify-between">
<div>
Expand All @@ -327,14 +339,14 @@ const RegisteredOperatorsPanel = ({
</div>

{operators.length === 0 ? (
<div className="flex min-h-40 items-center justify-center rounded-lg border border-dashed border-border bg-muted/30 p-8 text-center">
<div className="mt-5 flex min-h-40 items-center justify-center rounded-lg border border-dashed border-border bg-card p-8 text-center">
<p className="max-w-md text-muted-foreground text-sm">
No operators are indexed for this blueprint on the selected
network yet.
</p>
</div>
) : (
<div className="mt-5 divide-y divide-border overflow-hidden rounded-lg border border-border bg-muted/20">
<div className="mt-5 divide-y divide-border overflow-hidden rounded-lg border border-border bg-card">
{operators.map((operator) => (
<OperatorCard key={operator.address} operator={operator} />
))}
Expand Down
14 changes: 10 additions & 4 deletions apps/tangle-cloud/src/pages/blueprints/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ const Page: FC = () => {
<div className="space-y-6">
<Card
variant="sandbox"
className="cloud-hero-card cloud-compact-header overflow-hidden border-border bg-card shadow-[var(--shadow-card)]"
className="cloud-hero-card cloud-compact-header overflow-hidden"
>
<CardContent className="relative p-4 md:p-5">
<div className="pointer-events-none absolute inset-0 opacity-70 [background:radial-gradient(circle_at_12%_8%,rgba(99,102,241,0.22),transparent_32%),radial-gradient(circle_at_86%_12%,rgba(16,185,129,0.12),transparent_28%)]" />
Expand Down Expand Up @@ -206,7 +206,7 @@ const Page: FC = () => {
</div>
</div>

<div className="grid gap-3 rounded-lg border border-border bg-muted/15 p-3 shadow-[var(--shadow-card)] sm:grid-cols-[1fr_auto] sm:items-center xl:grid-cols-1">
<div className="grid gap-3 rounded-lg border border-border bg-[var(--bg-elevated)] p-3 shadow-[var(--shadow-card)] sm:grid-cols-[1fr_auto] sm:items-center xl:grid-cols-1">
<div className="grid grid-cols-3 gap-2">
<HeroMetric label="Catalog" value={blueprints.size} />
<HeroMetric
Expand Down Expand Up @@ -309,7 +309,7 @@ const HeroMetric = ({
label: string;
value: number | string;
}) => (
<div className="rounded-md border border-border bg-card/70 p-2.5">
<div className="rounded-md border border-border bg-[var(--bg-card)] p-2.5">
<p className="font-medium text-muted-foreground text-[10px] uppercase tracking-wider">
{label}
</p>
Expand All @@ -328,7 +328,13 @@ const HeroRolePill = ({
value: string;
detail: string;
}) => (
<div className="rounded-lg border border-border bg-muted/20 p-3">
<div
className="rounded-lg border p-3"
style={{
backgroundColor: 'var(--accent-surface-soft)',
borderColor: 'var(--border-accent)',
}}
>
<p className="font-semibold text-muted-foreground text-[10px] uppercase tracking-wider">
{label}
</p>
Expand Down
7 changes: 2 additions & 5 deletions apps/tangle-cloud/src/pages/instances/AccountStatsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,8 @@ export const AccountStatsCard: FC<AccountStatsCardProps> = (props) => {
if (!accountAddress) {
return (
<Card
variant="sandbox"
className={twMerge(
'w-full border-border bg-card shadow-[var(--shadow-card)]',
rootProps?.className,
)}
variant="elevated"
className={twMerge('w-full', rootProps?.className)}
>
<CardContent className="flex h-full flex-col gap-5 p-5 md:p-6">
<div className="flex min-w-0 items-start gap-4">
Expand Down
2 changes: 1 addition & 1 deletion apps/tangle-cloud/src/pages/instances/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const Page = () => {
<div className="space-y-6">
<Card
variant="sandbox"
className="cloud-hero-card cloud-compact-header overflow-hidden border-border bg-card shadow-[var(--shadow-card)]"
className="cloud-hero-card cloud-compact-header overflow-hidden"
>
<CardContent className="relative p-4 md:p-5">
<div className="pointer-events-none absolute inset-0 opacity-70 [background:radial-gradient(circle_at_12%_8%,rgba(99,102,241,0.18),transparent_32%),radial-gradient(circle_at_86%_12%,rgba(16,185,129,0.10),transparent_28%)]" />
Expand Down
Loading
Loading