Three card families: Collectibles (mint) + Agents (skill-bar back); New-card under cards
- localCardsStore: cards gain `kind` (business/collectible/agent) + optional provenance (collectibles) and agent meta (agents); cardsOfKind() filter. - Cards tab: "+ New card" moved to a footer below the cards (no floating FAB); filtered to business cards. - Collectibles + Agents tabs: real grids (CardCollection) with empty states and a "+ New" footer that opens the creator pre-set to that kind. - Creator (demo.tsx) is kind-aware: collectibles mint a provenance record on save (utils/provenance stub, mapped to the creator); agents capture flip-side details (AgentDetailsEditor: tagline, skill bars, tools, capabilities). - Card back per kind: AgentBackTemplate (skill bars/tools/capabilities) for agents; CardBackTemplate gains a "Minted · #token · chain" footer for collectibles. Both reuse the AI image/video front workflow. - tsc + 28 jest green. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
e77e812609
commit
d30deaf857
@@ -1,27 +1,15 @@
|
||||
// Work-agent cards — AI agents attached to your cards. None yet; this is their
|
||||
// home.
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
// Agents: cards for your AI assistants. Front is an AI-generated portrait; the
|
||||
// flip side shows skill bars, tools, and special capabilities.
|
||||
import { CardCollection } from "../../src/components/CardCollection";
|
||||
|
||||
export default function Agents() {
|
||||
const insets = useSafeAreaInsets();
|
||||
return (
|
||||
<View style={[styles.root, { paddingTop: insets.top }]}>
|
||||
<View style={styles.center}>
|
||||
<MaterialCommunityIcons name="robot" size={48} color="#3a3a44" />
|
||||
<Text style={styles.title}>No agent cards yet</Text>
|
||||
<Text style={styles.hint}>
|
||||
Cards backed by an AI work agent will appear here.
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<CardCollection
|
||||
kind="agent"
|
||||
heading="Agents"
|
||||
addLabel="+ New agent"
|
||||
emptyTitle="No agent cards yet"
|
||||
emptyHint="Generate a portrait of your assistant; flip it for skills & tools."
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: { flex: 1, backgroundColor: "#0a0a0c" },
|
||||
center: { flex: 1, alignItems: "center", justifyContent: "center", padding: 32, gap: 10 },
|
||||
title: { color: "#f5f5f7", fontSize: 20, fontWeight: "700" },
|
||||
hint: { color: "#6b6b70", fontSize: 14, textAlign: "center", lineHeight: 20 },
|
||||
});
|
||||
|
||||
@@ -1,27 +1,15 @@
|
||||
// Collectible cards — cards you've collected from people you meet (a deck you
|
||||
// build up). No collectibles yet; this is the home for them.
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
// Collectibles: AI-generated cards minted to the blockchain (provenance mapped
|
||||
// to the creator). Create flow reuses the card creator with kind=collectible.
|
||||
import { CardCollection } from "../../src/components/CardCollection";
|
||||
|
||||
export default function Collectibles() {
|
||||
const insets = useSafeAreaInsets();
|
||||
return (
|
||||
<View style={[styles.root, { paddingTop: insets.top }]}>
|
||||
<View style={styles.center}>
|
||||
<MaterialCommunityIcons name="sack" size={48} color="#3a3a44" />
|
||||
<Text style={styles.title}>No collectibles yet</Text>
|
||||
<Text style={styles.hint}>
|
||||
Cards you collect from people you meet will live here — build your deck.
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<CardCollection
|
||||
kind="collectible"
|
||||
heading="Collectibles"
|
||||
addLabel="+ New collectible"
|
||||
emptyTitle="No collectibles yet"
|
||||
emptyHint="Create one with AI — it's minted to the chain and mapped to you."
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: { flex: 1, backgroundColor: "#0a0a0c" },
|
||||
center: { flex: 1, alignItems: "center", justifyContent: "center", padding: 32, gap: 10 },
|
||||
title: { color: "#f5f5f7", fontSize: 20, fontWeight: "700" },
|
||||
hint: { color: "#6b6b70", fontSize: 14, textAlign: "center", lineHeight: 20 },
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useState } from "react";
|
||||
import { FlatList, Pressable, StyleSheet, Text, View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { CardThumb } from "../../src/components/CardThumb";
|
||||
import { LocalCard, useLocalCardsStore } from "../../src/stores/localCardsStore";
|
||||
import { cardsOfKind, LocalCard, useLocalCardsStore } from "../../src/stores/localCardsStore";
|
||||
|
||||
type ViewMode = "grid" | "showcase";
|
||||
|
||||
@@ -20,7 +20,10 @@ function columnsFor(count: number): number {
|
||||
export default function Gallery() {
|
||||
const router = useRouter();
|
||||
const insets = useSafeAreaInsets();
|
||||
const cards = useLocalCardsStore((s) => s.cards);
|
||||
const cards = cardsOfKind(
|
||||
useLocalCardsStore((s) => s.cards),
|
||||
"business",
|
||||
);
|
||||
const [view, setView] = useState<ViewMode>("grid");
|
||||
const columns = view === "showcase" ? 1 : columnsFor(cards.length);
|
||||
|
||||
@@ -44,11 +47,9 @@ export default function Gallery() {
|
||||
</View>
|
||||
}
|
||||
ListFooterComponent={
|
||||
view === "showcase" && cards.length > 0 ? (
|
||||
<Pressable style={styles.addTile} onPress={() => router.push("/demo")}>
|
||||
<Text style={styles.addTileText}>+ New card</Text>
|
||||
</Pressable>
|
||||
) : null
|
||||
<Pressable style={styles.addTile} onPress={() => router.push("/demo")}>
|
||||
<Text style={styles.addTileText}>+ New card</Text>
|
||||
</Pressable>
|
||||
}
|
||||
renderItem={({ item }: { item: LocalCard }) =>
|
||||
view === "showcase" ? (
|
||||
@@ -83,9 +84,6 @@ export default function Gallery() {
|
||||
}
|
||||
/>
|
||||
|
||||
<Pressable style={[styles.fab, { bottom: insets.bottom + 80 }]} onPress={() => router.push("/demo")}>
|
||||
<Text style={styles.fabText}>+ New card</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user