@hanzo/ui 8.0.1 → 8.0.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/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@hanzo/ui",
3
- "version": "8.0.1",
3
+ "version": "8.0.2",
4
4
  "type": "module",
5
- "description": "Hanzo UI \u2014 the one cross-platform component library on @hanzo/gui. The product/app layer (charts, metrics, page headers, status tags, rich empty states, combobox, slide-over, toasts, drag-reorder, labeled field rows, provider/product marks) + the metadata-driven record layer (@hanzo/data: RecordsView, DataTable, board, typed field editors) + the calm dark-first tokens and motion. Presentational, host-agnostic (data/effects injected), clean-room. Web + native (iOS) + desktop.",
5
+ "description": "Hanzo UI the one cross-platform component library on @hanzo/gui. The product/app layer (charts, metrics, page headers, status tags, rich empty states, combobox, slide-over, toasts, drag-reorder, labeled field rows, provider/product marks) + the metadata-driven record layer (@hanzo/data: RecordsView, DataTable, board, typed field editors) + the calm dark-first tokens and motion. Presentational, host-agnostic (data/effects injected), clean-room. Web + native (iOS) + desktop.",
6
6
  "exports": {
7
7
  ".": {
8
8
  "types": "./types/index.d.ts",
@@ -55,3 +55,9 @@ export * from './StatusTag'
55
55
  export * from './ThemeToggle'
56
56
  export * from './Toast'
57
57
  export * from './color'
58
+
59
+ // Usage — the shared AI-quota surface (meter bar → provider card → dashboard).
60
+ export * from './usage'
61
+
62
+ // Social — ChannelBadge, PostCard, CampaignCard (extracted from Hanzo Social).
63
+ export * from './social'
@@ -0,0 +1,62 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * CampaignCard — one marketing campaign (name, channel, lifecycle, budget/spend
5
+ * bar). Pure: all data via props. Reuses the shared StatusTag for the lifecycle
6
+ * pill (one status vocabulary). Extracted from Hanzo Social.
7
+ */
8
+ import { Card, Text, XStack, YStack } from '@hanzo/gui'
9
+ import { StatusTag } from '../StatusTag'
10
+
11
+ export type Campaign = {
12
+ id: string
13
+ name: string
14
+ channel: string
15
+ status: string
16
+ objective?: string
17
+ /** minor units (cents) */
18
+ budget: number
19
+ spend: number
20
+ }
21
+
22
+ const money = (cents: number) => `$${Math.round((cents || 0) / 100).toLocaleString()}`
23
+
24
+ export function CampaignCard({ campaign }: { campaign: Campaign }) {
25
+ const pct =
26
+ campaign.budget > 0 ? Math.min(100, (campaign.spend / campaign.budget) * 100) : 0
27
+ return (
28
+ <Card p="$4" gap="$3" borderWidth={1} borderColor="$borderColor" width="100%">
29
+ <XStack items="flex-start" justify="space-between" gap="$2">
30
+ <YStack gap="$1">
31
+ <Text fontSize="$4" fontWeight="700">
32
+ {campaign.name}
33
+ </Text>
34
+ <Text fontSize="$2" color="$color11">
35
+ {campaign.channel}
36
+ </Text>
37
+ </YStack>
38
+ <StatusTag status={campaign.status} />
39
+ </XStack>
40
+
41
+ {campaign.objective ? (
42
+ <Text fontSize="$2" color="$color11">
43
+ {campaign.objective}
44
+ </Text>
45
+ ) : null}
46
+
47
+ <YStack gap="$1.5">
48
+ <XStack justify="space-between">
49
+ <Text fontSize="$1" color="$color11">
50
+ {money(campaign.spend)} spent
51
+ </Text>
52
+ <Text fontSize="$1" color="$color11">
53
+ {money(campaign.budget)} budget
54
+ </Text>
55
+ </XStack>
56
+ <YStack height={6} rounded="$2" bg="$color3" overflow="hidden">
57
+ <YStack height={6} rounded="$2" bg="$color9" width={`${pct}%` as never} />
58
+ </YStack>
59
+ </YStack>
60
+ </Card>
61
+ )
62
+ }
@@ -0,0 +1,60 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * ChannelBadge — the ONE way a social network is marked (brand color + glyph +
5
+ * optional label), on @hanzo/gui primitives so it renders on web, native, and
6
+ * desktop. Data-free / host-agnostic. Extracted from Hanzo Social (social.hanzo.ai).
7
+ */
8
+ import { Text, XStack, YStack } from '@hanzo/gui'
9
+
10
+ export type Channel =
11
+ | 'x'
12
+ | 'facebook'
13
+ | 'instagram'
14
+ | 'linkedin'
15
+ | 'tiktok'
16
+ | 'youtube'
17
+ | 'threads'
18
+
19
+ const META: Record<Channel, { label: string; bg: string; fg: string; mark: string }> = {
20
+ x: { label: 'X', bg: '#0f0f12', fg: '#e6e6ea', mark: '𝕏' },
21
+ facebook: { label: 'Facebook', bg: '#1877f2', fg: '#ffffff', mark: 'f' },
22
+ instagram: { label: 'Instagram', bg: '#e1306c', fg: '#ffffff', mark: 'IG' },
23
+ linkedin: { label: 'LinkedIn', bg: '#0a66c2', fg: '#ffffff', mark: 'in' },
24
+ tiktok: { label: 'TikTok', bg: '#111114', fg: '#25f4ee', mark: 'TT' },
25
+ youtube: { label: 'YouTube', bg: '#ff0000', fg: '#ffffff', mark: '▶' },
26
+ threads: { label: 'Threads', bg: '#111114', fg: '#e6e6ea', mark: '@' },
27
+ }
28
+
29
+ export function ChannelBadge({
30
+ channel,
31
+ showLabel = false,
32
+ size = 22,
33
+ }: {
34
+ channel: Channel
35
+ showLabel?: boolean
36
+ size?: number
37
+ }) {
38
+ const m = META[channel] ?? META.x
39
+ return (
40
+ <XStack items="center" gap="$2">
41
+ <YStack
42
+ width={size}
43
+ height={size}
44
+ items="center"
45
+ justify="center"
46
+ rounded="$2"
47
+ bg={m.bg as never}
48
+ >
49
+ <Text fontSize="$1" fontWeight="800" color={m.fg as never}>
50
+ {m.mark}
51
+ </Text>
52
+ </YStack>
53
+ {showLabel ? (
54
+ <Text fontSize="$3" color="$color12">
55
+ {m.label}
56
+ </Text>
57
+ ) : null}
58
+ </XStack>
59
+ )
60
+ }
@@ -0,0 +1,80 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * PostCard — one social post: channel, lifecycle, body, schedule time, a media
5
+ * count, and injected edit/delete affordances. Pure (data + handlers via props);
6
+ * composes ChannelBadge + the shared StatusTag. Extracted from Hanzo Social.
7
+ */
8
+ import { Card, Text, XStack, YStack, Button } from '@hanzo/gui'
9
+ import { Pencil, Trash2 } from '@hanzogui/lucide-icons-2'
10
+ import { StatusTag } from '../StatusTag'
11
+ import { ChannelBadge, type Channel } from './ChannelBadge'
12
+
13
+ export type Post = {
14
+ id: string
15
+ content: string
16
+ channel: Channel
17
+ status: string
18
+ /** unix seconds; 0/undefined = not scheduled */
19
+ scheduleAt?: number
20
+ media?: string[]
21
+ }
22
+
23
+ const fmt = (unix?: number) =>
24
+ unix
25
+ ? new Date(unix * 1000).toLocaleString(undefined, {
26
+ month: 'short',
27
+ day: 'numeric',
28
+ hour: '2-digit',
29
+ minute: '2-digit',
30
+ })
31
+ : ''
32
+
33
+ export function PostCard({
34
+ post,
35
+ onEdit,
36
+ onDelete,
37
+ }: {
38
+ post: Post
39
+ onEdit?: (p: Post) => void
40
+ onDelete?: (p: Post) => void
41
+ }) {
42
+ const mediaCount = post.media?.length ?? 0
43
+ return (
44
+ <Card p="$3" gap="$2.5" borderWidth={1} borderColor="$borderColor" width="100%">
45
+ <XStack items="center" justify="space-between" gap="$2">
46
+ <XStack items="center" gap="$2">
47
+ <ChannelBadge channel={post.channel} />
48
+ <StatusTag status={post.status} />
49
+ </XStack>
50
+ <XStack gap="$1">
51
+ {onEdit ? (
52
+ <Button size="$2" chromeless icon={<Pencil size={15} />} onPress={() => onEdit(post)} />
53
+ ) : null}
54
+ {onDelete ? (
55
+ <Button size="$2" chromeless icon={<Trash2 size={15} />} onPress={() => onDelete(post)} />
56
+ ) : null}
57
+ </XStack>
58
+ </XStack>
59
+
60
+ <Text fontSize="$3" color="$color12">
61
+ {post.content}
62
+ </Text>
63
+
64
+ <XStack items="center" justify="space-between" gap="$2">
65
+ {post.scheduleAt ? (
66
+ <Text fontSize="$1" color="$color11">
67
+ {fmt(post.scheduleAt)}
68
+ </Text>
69
+ ) : (
70
+ <YStack />
71
+ )}
72
+ {mediaCount ? (
73
+ <Text fontSize="$1" color="$color10">
74
+ {mediaCount} media
75
+ </Text>
76
+ ) : null}
77
+ </XStack>
78
+ </Card>
79
+ )
80
+ }
@@ -0,0 +1,6 @@
1
+ // @hanzo/ui/product/social — pure, host-agnostic social-media components
2
+ // extracted from Hanzo Social (social.hanzo.ai). Data + handlers injected via
3
+ // props; built only on @hanzo/gui primitives + the shared StatusTag.
4
+ export * from './ChannelBadge'
5
+ export * from './CampaignCard'
6
+ export * from './PostCard'
@@ -0,0 +1,54 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Usage dashboard — the ONE shared AI-usage screen every Hanzo app (console,
5
+ * desktop, app, chat) renders: a summary header of totals (spend / tokens /
6
+ * requests) as `Metric` tiles, above a responsive grid of `UsageProviderCard`s.
7
+ *
8
+ * Presentational + honest: totals are pre-summed by the caller and formatted here
9
+ * (an absent total is an em-dash, never fabricated); the grid simply lays out the
10
+ * providers it is given. @hanzo/gui primitives only — web + native + desktop.
11
+ */
12
+ import { XStack, YStack } from '@hanzo/gui'
13
+ import { Activity, Coins, DollarSign } from '@hanzogui/lucide-icons-2'
14
+
15
+ import { MetricCard } from '../Metric'
16
+ import { UsageProviderCard, type UsageProviderCardProps, formatUsageCount, formatUsageCurrency } from './UsageProviderCard'
17
+
18
+ export function UsageDashboard({
19
+ providers,
20
+ totals,
21
+ }: {
22
+ providers: UsageProviderCardProps[]
23
+ totals?: { spendUSD?: number; tokens?: number; requests?: number }
24
+ }) {
25
+ return (
26
+ <YStack gap="$4">
27
+ {totals ? (
28
+ <XStack gap="$3" flexWrap="wrap">
29
+ <MetricCard
30
+ icon={<DollarSign size={16} color="$color11" />}
31
+ label="Total spend"
32
+ value={totals.spendUSD != null ? formatUsageCurrency(totals.spendUSD, 'USD') : '—'}
33
+ />
34
+ <MetricCard
35
+ icon={<Coins size={16} color="$color11" />}
36
+ label="Tokens"
37
+ value={totals.tokens != null ? formatUsageCount(totals.tokens) : '—'}
38
+ />
39
+ <MetricCard
40
+ icon={<Activity size={16} color="$color11" />}
41
+ label="Requests"
42
+ value={totals.requests != null ? formatUsageCount(totals.requests) : '—'}
43
+ />
44
+ </XStack>
45
+ ) : null}
46
+
47
+ <XStack gap="$3" flexWrap="wrap">
48
+ {providers.map((p) => (
49
+ <UsageProviderCard key={p.name} {...p} />
50
+ ))}
51
+ </XStack>
52
+ </YStack>
53
+ )
54
+ }
@@ -0,0 +1,75 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Usage meter — the ONE labeled rate-limit bar every Hanzo app renders for an AI
5
+ * quota (session / weekly / a custom window). A thin track + tone-by-value fill
6
+ * (green calm → amber → red hot, the same `utilColor` semantics as the console's
7
+ * `UtilBar`), a "% left" read-out, and an honest reset countdown ("resets in 2h
8
+ * 14m") computed from a real `resetsAt` — never a fabricated number.
9
+ *
10
+ * Built only from @hanzo/gui primitives (the nested-YStack track/fill is the house
11
+ * bar idiom from `Charts.BarRows`), so it themes to the shell and works web +
12
+ * native + desktop. No DOM APIs: `Date` only.
13
+ */
14
+ import { Text, XStack, YStack } from '@hanzo/gui'
15
+
16
+ import { utilColor } from '../Metric'
17
+
18
+ /** A single quota window: how much is spent (0–100) and when it refills. */
19
+ export type UsageWindow = {
20
+ usedPercent: number
21
+ /** ISO string or Date the window resets — drives the honest countdown. */
22
+ resetsAt?: string | Date
23
+ }
24
+
25
+ /** "resets in 2h 14m" from a real reset time, or null when there is none/past. */
26
+ export function resetCountdown(resetsAt?: string | Date): string | null {
27
+ if (resetsAt == null) return null
28
+ const t = typeof resetsAt === 'string' ? Date.parse(resetsAt) : resetsAt.getTime()
29
+ if (!Number.isFinite(t)) return null
30
+ const mins = Math.floor((t - Date.now()) / 60000)
31
+ if (mins <= 0) return 'resets now'
32
+ const d = Math.floor(mins / 1440)
33
+ const h = Math.floor((mins % 1440) / 60)
34
+ const m = mins % 60
35
+ if (d > 0) return `resets in ${d}d ${h}h`
36
+ if (h > 0) return `resets in ${h}h ${m}m`
37
+ return `resets in ${m}m`
38
+ }
39
+
40
+ export function UsageMeter({
41
+ label,
42
+ usedPercent,
43
+ resetsAt,
44
+ compact,
45
+ }: {
46
+ label: string
47
+ usedPercent: number
48
+ resetsAt?: string | Date
49
+ compact?: boolean
50
+ }) {
51
+ const used = Math.max(0, Math.min(100, usedPercent))
52
+ const left = Math.round(100 - used)
53
+ const reset = resetCountdown(resetsAt)
54
+ const h = compact ? 6 : 8
55
+ return (
56
+ <YStack gap={compact ? '$1' : '$1.5'}>
57
+ <XStack items="center" justify="space-between" gap="$2">
58
+ <Text fontSize="$2" color="$color11" numberOfLines={1} flex={1}>
59
+ {label}
60
+ </Text>
61
+ <Text fontSize="$2" color="$color12" fontWeight="600">
62
+ {left}% left
63
+ </Text>
64
+ </XStack>
65
+ <YStack height={h} bg="$color3" rounded="$2" overflow="hidden">
66
+ <YStack height={h} width={`${Math.max(2, used)}%`} bg={utilColor(used) as never} rounded="$2" />
67
+ </YStack>
68
+ {reset && !compact ? (
69
+ <Text fontSize="$1" color="$color10">
70
+ {reset}
71
+ </Text>
72
+ ) : null}
73
+ </YStack>
74
+ )
75
+ }
@@ -0,0 +1,153 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Usage provider card — one AI provider's quota at a glance, mirroring the Codex
5
+ * menu card: a session bar, a weekly bar, any extra windows, credits/spend, and a
6
+ * used-% history Sparkline. Composes `UsageMeter` rows + the canonical
7
+ * `Charts.Sparkline` + `Metric` idioms; nothing is fabricated (a missing window
8
+ * renders no row, a <2-point history renders no spark, `error`/`sourceLabel`/
9
+ * `updatedAt` surface honest provenance).
10
+ *
11
+ * @hanzo/gui primitives only — themes to the shell, works web + native + desktop.
12
+ */
13
+ import { Card, Text, XStack, YStack } from '@hanzo/gui'
14
+
15
+ import { Sparkline } from '../Charts'
16
+ import { UsageMeter, type UsageWindow } from './UsageMeter'
17
+
18
+ /** Format a monetary amount; falls back to "12.34 USD" where Intl currency is absent. */
19
+ export function formatUsageCurrency(value: number, currencyCode: string): string {
20
+ try {
21
+ return new Intl.NumberFormat(undefined, { style: 'currency', currency: currencyCode, maximumFractionDigits: 2 }).format(value)
22
+ } catch {
23
+ return `${value.toFixed(2)} ${currencyCode}`
24
+ }
25
+ }
26
+
27
+ /** Compact count (1.2K / 3.4M / 5.6B) for token/request totals. */
28
+ export function formatUsageCount(n: number): string {
29
+ if (!Number.isFinite(n)) return '—'
30
+ const abs = Math.abs(n)
31
+ if (abs >= 1e9) return `${(n / 1e9).toFixed(1)}B`
32
+ if (abs >= 1e6) return `${(n / 1e6).toFixed(1)}M`
33
+ if (abs >= 1e3) return `${(n / 1e3).toFixed(1)}K`
34
+ return String(Math.round(n))
35
+ }
36
+
37
+ /** "updated 2m ago" from a real timestamp, or null when there is none. */
38
+ function updatedLabel(t?: string | Date): string | null {
39
+ if (t == null) return null
40
+ const ms = typeof t === 'string' ? Date.parse(t) : t.getTime()
41
+ if (!Number.isFinite(ms)) return null
42
+ const mins = Math.floor((Date.now() - ms) / 60000)
43
+ if (mins < 1) return 'updated just now'
44
+ if (mins < 60) return `updated ${mins}m ago`
45
+ const h = Math.floor(mins / 60)
46
+ if (h < 24) return `updated ${h}h ago`
47
+ return `updated ${Math.floor(h / 24)}d ago`
48
+ }
49
+
50
+ export type UsageProviderCardProps = {
51
+ name: string
52
+ /** Accent dot color (hex/rgb) — the provider's brand mark. */
53
+ color?: string
54
+ /** Plan/tier badge (e.g. "Pro", "Team"). */
55
+ plan?: string
56
+ session?: UsageWindow
57
+ weekly?: UsageWindow
58
+ /** Additional named windows (e.g. per-model or per-tool quotas). */
59
+ extras?: Array<{ id: string; title: string; usedPercent: number; resetsAt?: string | Date }>
60
+ spend?: { used: number; limit?: number; currencyCode: string }
61
+ /** Used-% samples over time — drawn with the canonical Sparkline. */
62
+ history?: number[]
63
+ /** An honest load/refresh error for this provider. */
64
+ error?: string
65
+ /** Where the numbers came from (e.g. "OAuth", "API key"). */
66
+ sourceLabel?: string
67
+ updatedAt?: string | Date
68
+ }
69
+
70
+ export function UsageProviderCard({
71
+ name,
72
+ color,
73
+ plan,
74
+ session,
75
+ weekly,
76
+ extras,
77
+ spend,
78
+ history,
79
+ error,
80
+ sourceLabel,
81
+ updatedAt,
82
+ }: UsageProviderCardProps) {
83
+ const updated = updatedLabel(updatedAt)
84
+ return (
85
+ <Card p="$4" gap="$3" borderWidth={1} borderColor="$borderColor" flex={1} minW={260}>
86
+ <XStack items="center" justify="space-between" gap="$2">
87
+ <XStack items="center" gap="$2" flex={1}>
88
+ {color ? <YStack width={10} height={10} rounded="$2" bg={color as never} /> : null}
89
+ <Text fontSize="$4" fontWeight="800" color="$color12" numberOfLines={1}>
90
+ {name}
91
+ </Text>
92
+ </XStack>
93
+ {plan ? (
94
+ <Text fontSize="$1" px="$2" py="$1" rounded="$2" bg="$color3" color="$color11">
95
+ {plan}
96
+ </Text>
97
+ ) : null}
98
+ </XStack>
99
+
100
+ {error ? (
101
+ <Text fontSize="$2" color="#e5534b">
102
+ {error}
103
+ </Text>
104
+ ) : null}
105
+
106
+ {session ? <UsageMeter label="Session" usedPercent={session.usedPercent} resetsAt={session.resetsAt} /> : null}
107
+ {weekly ? <UsageMeter label="Weekly" usedPercent={weekly.usedPercent} resetsAt={weekly.resetsAt} /> : null}
108
+ {extras?.map((e) => (
109
+ <UsageMeter key={e.id} label={e.title} usedPercent={e.usedPercent} resetsAt={e.resetsAt} />
110
+ ))}
111
+
112
+ {spend ? (
113
+ spend.limit != null ? (
114
+ <UsageMeter
115
+ label={`Spend · ${formatUsageCurrency(spend.used, spend.currencyCode)} / ${formatUsageCurrency(spend.limit, spend.currencyCode)}`}
116
+ usedPercent={spend.limit > 0 ? (spend.used / spend.limit) * 100 : 0}
117
+ compact
118
+ />
119
+ ) : (
120
+ <XStack items="center" justify="space-between" gap="$2">
121
+ <Text fontSize="$2" color="$color11">
122
+ Spend
123
+ </Text>
124
+ <Text fontSize="$2" color="$color12" fontWeight="600">
125
+ {formatUsageCurrency(spend.used, spend.currencyCode)}
126
+ </Text>
127
+ </XStack>
128
+ )
129
+ ) : null}
130
+
131
+ {history && history.length >= 2 ? (
132
+ <XStack justify="flex-end">
133
+ <Sparkline values={history} />
134
+ </XStack>
135
+ ) : null}
136
+
137
+ {sourceLabel || updated ? (
138
+ <XStack items="center" justify="space-between" gap="$2">
139
+ {sourceLabel ? (
140
+ <Text fontSize="$1" color="$color10" numberOfLines={1}>
141
+ {sourceLabel}
142
+ </Text>
143
+ ) : null}
144
+ {updated ? (
145
+ <Text fontSize="$1" color="$color10">
146
+ {updated}
147
+ </Text>
148
+ ) : null}
149
+ </XStack>
150
+ ) : null}
151
+ </Card>
152
+ )
153
+ }
@@ -0,0 +1,10 @@
1
+ // @hanzo/ui/product/usage — the shared AI-usage surface (the ONE way every Hanzo
2
+ // app renders quota bars, per-provider cards, and the usage dashboard).
3
+ //
4
+ // UsageMeter (labeled rate-limit bar) → UsageProviderCard (one provider) →
5
+ // UsageDashboard (totals header + provider grid). Presentational, host-agnostic,
6
+ // cross-platform on @hanzo/gui.
7
+
8
+ export * from './UsageMeter'
9
+ export * from './UsageProviderCard'
10
+ export * from './UsageDashboard'
@@ -19,4 +19,6 @@ export * from './StatusTag';
19
19
  export * from './ThemeToggle';
20
20
  export * from './Toast';
21
21
  export * from './color';
22
+ export * from './usage';
23
+ export * from './social';
22
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/product/index.ts"],"names":[],"mappings":"AAaA,cAAc,UAAU,CAAA;AAKxB,OAAO,EACL,MAAM,EACN,aAAa,EACb,SAAS,EACT,SAAS,IAAI,eAAe,EAC5B,QAAQ,EACR,OAAO,EACP,SAAS,EACT,UAAU,EACV,UAAU,EACV,KAAK,GACN,MAAM,UAAU,CAAA;AAGjB,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,SAAS,CAAA;AAK/D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,cAAc,mBAAmB,CAAA;AAGjC,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,SAAS,CAAA;AACvB,cAAc,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/product/index.ts"],"names":[],"mappings":"AAaA,cAAc,UAAU,CAAA;AAKxB,OAAO,EACL,MAAM,EACN,aAAa,EACb,SAAS,EACT,SAAS,IAAI,eAAe,EAC5B,QAAQ,EACR,OAAO,EACP,SAAS,EACT,UAAU,EACV,UAAU,EACV,KAAK,GACN,MAAM,UAAU,CAAA;AAGjB,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,SAAS,CAAA;AAK/D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,cAAc,mBAAmB,CAAA;AAGjC,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,SAAS,CAAA;AACvB,cAAc,SAAS,CAAA;AAGvB,cAAc,SAAS,CAAA;AAGvB,cAAc,UAAU,CAAA"}
@@ -0,0 +1,14 @@
1
+ export type Campaign = {
2
+ id: string;
3
+ name: string;
4
+ channel: string;
5
+ status: string;
6
+ objective?: string;
7
+ /** minor units (cents) */
8
+ budget: number;
9
+ spend: number;
10
+ };
11
+ export declare function CampaignCard({ campaign }: {
12
+ campaign: Campaign;
13
+ }): import("react").JSX.Element;
14
+ //# sourceMappingURL=CampaignCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CampaignCard.d.ts","sourceRoot":"","sources":["../../../src/product/social/CampaignCard.tsx"],"names":[],"mappings":"AAUA,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0BAA0B;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAID,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,+BAsChE"}
@@ -0,0 +1,7 @@
1
+ export type Channel = 'x' | 'facebook' | 'instagram' | 'linkedin' | 'tiktok' | 'youtube' | 'threads';
2
+ export declare function ChannelBadge({ channel, showLabel, size, }: {
3
+ channel: Channel;
4
+ showLabel?: boolean;
5
+ size?: number;
6
+ }): import("react").JSX.Element;
7
+ //# sourceMappingURL=ChannelBadge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChannelBadge.d.ts","sourceRoot":"","sources":["../../../src/product/social/ChannelBadge.tsx"],"names":[],"mappings":"AASA,MAAM,MAAM,OAAO,GACf,GAAG,GACH,UAAU,GACV,WAAW,GACX,UAAU,GACV,QAAQ,GACR,SAAS,GACT,SAAS,CAAA;AAYb,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,SAAiB,EACjB,IAAS,GACV,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,+BAuBA"}
@@ -0,0 +1,16 @@
1
+ import { type Channel } from './ChannelBadge';
2
+ export type Post = {
3
+ id: string;
4
+ content: string;
5
+ channel: Channel;
6
+ status: string;
7
+ /** unix seconds; 0/undefined = not scheduled */
8
+ scheduleAt?: number;
9
+ media?: string[];
10
+ };
11
+ export declare function PostCard({ post, onEdit, onDelete, }: {
12
+ post: Post;
13
+ onEdit?: (p: Post) => void;
14
+ onDelete?: (p: Post) => void;
15
+ }): import("react").JSX.Element;
16
+ //# sourceMappingURL=PostCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PostCard.d.ts","sourceRoot":"","sources":["../../../src/product/social/PostCard.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAE3D,MAAM,MAAM,IAAI,GAAG;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAYD,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,MAAM,EACN,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,IAAI,CAAA;IAC1B,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,IAAI,CAAA;CAC7B,+BAuCA"}
@@ -0,0 +1,4 @@
1
+ export * from './ChannelBadge';
2
+ export * from './CampaignCard';
3
+ export * from './PostCard';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/product/social/index.ts"],"names":[],"mappings":"AAGA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA"}
@@ -0,0 +1,10 @@
1
+ import { type UsageProviderCardProps } from './UsageProviderCard';
2
+ export declare function UsageDashboard({ providers, totals, }: {
3
+ providers: UsageProviderCardProps[];
4
+ totals?: {
5
+ spendUSD?: number;
6
+ tokens?: number;
7
+ requests?: number;
8
+ };
9
+ }): import("react").JSX.Element;
10
+ //# sourceMappingURL=UsageDashboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UsageDashboard.d.ts","sourceRoot":"","sources":["../../../src/product/usage/UsageDashboard.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAqB,KAAK,sBAAsB,EAAyC,MAAM,qBAAqB,CAAA;AAE3H,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,MAAM,GACP,EAAE;IACD,SAAS,EAAE,sBAAsB,EAAE,CAAA;IACnC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACnE,+BA8BA"}
@@ -0,0 +1,15 @@
1
+ /** A single quota window: how much is spent (0–100) and when it refills. */
2
+ export type UsageWindow = {
3
+ usedPercent: number;
4
+ /** ISO string or Date the window resets — drives the honest countdown. */
5
+ resetsAt?: string | Date;
6
+ };
7
+ /** "resets in 2h 14m" from a real reset time, or null when there is none/past. */
8
+ export declare function resetCountdown(resetsAt?: string | Date): string | null;
9
+ export declare function UsageMeter({ label, usedPercent, resetsAt, compact, }: {
10
+ label: string;
11
+ usedPercent: number;
12
+ resetsAt?: string | Date;
13
+ compact?: boolean;
14
+ }): import("react").JSX.Element;
15
+ //# sourceMappingURL=UsageMeter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UsageMeter.d.ts","sourceRoot":"","sources":["../../../src/product/usage/UsageMeter.tsx"],"names":[],"mappings":"AAiBA,4EAA4E;AAC5E,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB,CAAA;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAYtE;AAED,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,WAAW,EACX,QAAQ,EACR,OAAO,GACR,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,+BAyBA"}
@@ -0,0 +1,35 @@
1
+ import { type UsageWindow } from './UsageMeter';
2
+ /** Format a monetary amount; falls back to "12.34 USD" where Intl currency is absent. */
3
+ export declare function formatUsageCurrency(value: number, currencyCode: string): string;
4
+ /** Compact count (1.2K / 3.4M / 5.6B) for token/request totals. */
5
+ export declare function formatUsageCount(n: number): string;
6
+ export type UsageProviderCardProps = {
7
+ name: string;
8
+ /** Accent dot color (hex/rgb) — the provider's brand mark. */
9
+ color?: string;
10
+ /** Plan/tier badge (e.g. "Pro", "Team"). */
11
+ plan?: string;
12
+ session?: UsageWindow;
13
+ weekly?: UsageWindow;
14
+ /** Additional named windows (e.g. per-model or per-tool quotas). */
15
+ extras?: Array<{
16
+ id: string;
17
+ title: string;
18
+ usedPercent: number;
19
+ resetsAt?: string | Date;
20
+ }>;
21
+ spend?: {
22
+ used: number;
23
+ limit?: number;
24
+ currencyCode: string;
25
+ };
26
+ /** Used-% samples over time — drawn with the canonical Sparkline. */
27
+ history?: number[];
28
+ /** An honest load/refresh error for this provider. */
29
+ error?: string;
30
+ /** Where the numbers came from (e.g. "OAuth", "API key"). */
31
+ sourceLabel?: string;
32
+ updatedAt?: string | Date;
33
+ };
34
+ export declare function UsageProviderCard({ name, color, plan, session, weekly, extras, spend, history, error, sourceLabel, updatedAt, }: UsageProviderCardProps): import("react").JSX.Element;
35
+ //# sourceMappingURL=UsageProviderCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UsageProviderCard.d.ts","sourceRoot":"","sources":["../../../src/product/usage/UsageProviderCard.tsx"],"names":[],"mappings":"AAeA,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAE3D,yFAAyF;AACzF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAM/E;AAED,mEAAmE;AACnE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAOlD;AAeD,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,oEAAoE;IACpE,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAA;IAC5F,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9D,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B,CAAA;AAED,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,OAAO,EACP,MAAM,EACN,MAAM,EACN,KAAK,EACL,OAAO,EACP,KAAK,EACL,WAAW,EACX,SAAS,GACV,EAAE,sBAAsB,+BAuExB"}
@@ -0,0 +1,4 @@
1
+ export * from './UsageMeter';
2
+ export * from './UsageProviderCard';
3
+ export * from './UsageDashboard';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/product/usage/index.ts"],"names":[],"mappings":"AAOA,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAA;AACnC,cAAc,kBAAkB,CAAA"}