Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1190218
Initial plan
Copilot Jul 30, 2026
ad63afb
feat: multi-buyer support in AI Credits widget
Copilot Jul 30, 2026
3494f60
feat: implement deep link handling for buyer registration in AI Credi…
blueogin Jul 31, 2026
dad5b67
feat: enhance deep link handling in AI Credits widget
blueogin Jul 31, 2026
6895f09
refactor: streamline buyer key generation in AI Credits widget
blueogin Jul 31, 2026
c87072f
refactor: update buyer management in AI Credits widget
blueogin Aug 3, 2026
5feb471
refactor: update buyer management to support deep-link functionality
blueogin Aug 3, 2026
1fad045
refactor: enhance buyer management and state handling in AI Credits w…
blueogin Aug 3, 2026
4e4af8c
Merge branch 'main' into copilot/multi-buyer-support-ai-credits-widge…
blueogin Aug 3, 2026
b21523c
feat(ai-credits-widget): enhance buyer management with consent tracki…
blueogin Aug 3, 2026
187ea41
feat(ai-credits-widget): add discover buyers functionality and enhanc…
blueogin Aug 4, 2026
0832e0b
Merge branch 'main' into copilot/multi-buyer-support-ai-credits-widge…
blueogin Aug 4, 2026
e95164f
feat(ai-credits-widget): add derivedBuyerAddress to state management …
blueogin Aug 5, 2026
95b5a20
feat(ai-credits-widget): add operator consent pending state management
blueogin Aug 5, 2026
9a77493
fix(ai-credits-widget): update payment status checks to include 'paym…
blueogin Aug 5, 2026
8103f42
feat(ai-credits-widget): enhance buyer address management and private…
blueogin Aug 5, 2026
1250753
refactor(ai-credits-widget): remove buyerAddress from credit history …
blueogin Aug 5, 2026
ab11f64
refactor(ai-credits-widget): remove private key handling from state a…
blueogin Aug 5, 2026
4f69871
refactor(ai-credits-widget): streamline session management by removin…
blueogin Aug 5, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import {
BackendUnavailableStory,
UnsupportedChainStory,
AppKitConnectWalletStory,
MultiBuyerManageStory,
DeepLinkBuyerStory,
MultiBuyerHistoryStory,
} from '../helpers/aiCreditsWidgetStories'

const meta: Meta<typeof AiCreditsWidget> = {
Expand Down Expand Up @@ -91,6 +94,21 @@ export const UnsupportedChain: Story = {
render: () => <UnsupportedChainStory />,
}

/** Multi-buyer manage tab: buyer selector and private-key reveal. */
export const MultiBuyerManage: Story = {
render: () => <MultiBuyerManageStory />,
}

/** Deep-link partner buyer: consent via pre-signed operatorSignature. */
export const DeepLinkBuyer: Story = {
render: () => <DeepLinkBuyerStory />,
}

/** History tab with buyer filter dropdown. */
export const MultiBuyerHistory: Story = {
render: () => <MultiBuyerHistoryStory />,
}

export const AppKitConnectWallet: Story = {
render: () => <AppKitConnectWalletStory />,
play: async ({ canvasElement }) => {
Expand Down
87 changes: 87 additions & 0 deletions examples/storybook/src/stories/helpers/aiCreditsWidgetStories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ function createMockState(
isGoodIdVerified: false,
buyerPubKey: null,
buyerPrvKey: null,
operatorSignature: null,
operatorConsented: false,
operatorConsentPending: false,
operatorAddress: null,
minDepositUsd: '1.00',
minStreamUsd: '1.00',
Expand All @@ -45,6 +47,8 @@ function createMockState(
streamBonusPercent: 20,
error: null,
activeTab: 'buy',
buyers: [],
derivedBuyerAddress: null,
}
return { ...base, ...overrides }
}
Expand All @@ -59,6 +63,10 @@ function createAdapterFactory(
connect: async () => {},
switchChain: async () => {},
generateBuyerKey: async () => {},
selectBuyer: async () => {},
discoverBuyers: () => {},
importBuyerFromPrivateKey: async () => {},
applyDeepLinkBuyer: async () => {},
signOperatorConsent: async () => {},
syncOperatorConsentFromChain: async () => {},
buildQuote: async (depositG, streamG) => ({
Expand Down Expand Up @@ -412,3 +420,82 @@ export function InjectedWalletStory() {
</YStack>
)
}

// ---------------------------------------------------------------------------
// Multi-buyer fixture stories
// ---------------------------------------------------------------------------

const BUYER_WALLET = {
address: '0xfc128652c9b397a1f89A9EC84E798B869B0E4c7a' as const,
privateKey: '0x0000000000000000000000000000000000000000000000000000000000000001' as const,
}

const BUYER_IMPORTED = {
address: '0xAbcDef1234567890AbcDef1234567890AbcDef12' as const,
privateKey: '0x0000000000000000000000000000000000000000000000000000000000000002' as const,
}

const BUYER_PARTNER = {
address: '0x1111111111111111111111111111111111111111' as const,
operatorSignature:
'0x1111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222200' as const,
}

/** Multi-buyer manage: backend address list with one selected buyer that has a local key. */
export function MultiBuyerManageStory() {
return (
<MockStoryShell
dataTestId="AiCreditsWidget-multi-buyer-manage"
adapterFactory={createAdapterFactory('quote_ready', {
totalCreditUsd: '110000000',
buyerPubKey: BUYER_WALLET.address,
buyerPrvKey: BUYER_WALLET.privateKey,
operatorConsented: true,
operatorAddress: '0x0000000000000000000000000000000000000004',
totalGdDepositedG: '50.00',
monthlyStreamG: '5.00',
gBalance: '42.50',
activeTab: 'manage',
buyers: [BUYER_WALLET.address, BUYER_IMPORTED.address, BUYER_PARTNER.address],
derivedBuyerAddress: BUYER_WALLET.address,
})}
/>
)
}

/** Deep-link partner buyer: consent uses pre-signed operatorSignature (no private key). */
export function DeepLinkBuyerStory() {
return (
<MockStoryShell
dataTestId="AiCreditsWidget-deep-link-buyer"
adapterFactory={createAdapterFactory('purchase_setup', {
buyerPubKey: BUYER_PARTNER.address,
buyerPrvKey: null,
operatorSignature: BUYER_PARTNER.operatorSignature,
operatorConsented: false,
gBalance: '42.50',
activeTab: 'manage',
buyers: [BUYER_PARTNER.address],
})}
/>
)
}

/** History tab with multi-buyer filter options available. */
export function MultiBuyerHistoryStory() {
return (
<MockStoryShell
dataTestId="AiCreditsWidget-multi-buyer-history"
adapterFactory={createAdapterFactory('quote_ready', {
totalCreditUsd: '110000000',
buyerPubKey: BUYER_WALLET.address,
buyerPrvKey: BUYER_WALLET.privateKey,
operatorConsented: true,
gBalance: '42.50',
activeTab: 'history',
buyers: [BUYER_WALLET.address, BUYER_IMPORTED.address],
derivedBuyerAddress: BUYER_WALLET.address,
})}
/>
)
}
17 changes: 16 additions & 1 deletion packages/ai-credits-widget/src/AiCreditsWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ function BuyCreditsPanel({ state, actions, isPending, onPay }: BuyPanelProps) {
} else {
content = (
<>
{state.error && (
<AiCreditsStatusNotice>
<Text color="$error" fontWeight="700">
Deep link unavailable
</Text>
<Text secondary>{state.error}</Text>
</AiCreditsStatusNotice>
)}

{state.address && (
<AiCreditsHero
gBalance={state.gBalance}
Expand Down Expand Up @@ -323,8 +332,10 @@ function AiCreditsInner({
const history = useAiCreditsHistory({
address: state.address,
backendUrl,
defaultBuyerFilter: state.buyerPubKey ?? 'all',
environment,
backendClient: adapterOptions?.backendClient,
onBuyersDiscovered: actions.discoverBuyers,
})

const handlePay = useCallback(
Expand Down Expand Up @@ -396,7 +407,11 @@ function AiCreditsInner({
{state.activeTab === 'manage' ? (
<ManagePanel state={state} actions={actions} />
) : state.activeTab === 'history' ? (
<HistoryTab state={history.state} actions={history.actions} />
<HistoryTab
state={history.state}
actions={history.actions}
knownBuyers={state.buyers.map((address) => ({ address }))}
/>
) : (
buyPanel
)}
Expand Down
Loading
Loading