@faskai/ui-commons 0.0.0-alpha.3 → 0.0.0-alpha.30

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.
Files changed (42) hide show
  1. package/README.md +114 -14
  2. package/dist/components/connections/app-icon.d.ts +8 -0
  3. package/dist/components/connections/app-icon.js +21 -0
  4. package/dist/components/connections/app-search-grid.d.ts +10 -0
  5. package/dist/components/connections/app-search-grid.js +35 -0
  6. package/dist/components/connections/available-apps-list.d.ts +8 -0
  7. package/dist/components/connections/available-apps-list.js +15 -0
  8. package/dist/components/connections/connected-apps-list.d.ts +11 -0
  9. package/dist/components/connections/connected-apps-list.js +21 -0
  10. package/dist/components/connections/connections-api-provider.d.ts +9 -0
  11. package/dist/components/connections/connections-api-provider.js +14 -0
  12. package/dist/components/connections/connections-panel.d.ts +7 -0
  13. package/dist/components/connections/connections-panel.js +97 -0
  14. package/dist/components/connections/launch-nango-connect.d.ts +13 -0
  15. package/dist/components/connections/launch-nango-connect.js +63 -0
  16. package/dist/components/gtm/gtm-provider.d.ts +7 -0
  17. package/dist/components/gtm/gtm-provider.js +11 -0
  18. package/dist/components/layout/grid.d.ts +10 -0
  19. package/dist/components/layout/grid.js +11 -0
  20. package/dist/components/layout/row.d.ts +7 -0
  21. package/dist/components/layout/row.js +16 -0
  22. package/dist/components/layout/split-view.d.ts +6 -0
  23. package/dist/components/layout/split-view.js +6 -0
  24. package/dist/components/layout/stack.d.ts +6 -0
  25. package/dist/components/layout/stack.js +16 -0
  26. package/dist/components/ui/badge.d.ts +9 -0
  27. package/dist/components/ui/badge.js +22 -0
  28. package/dist/components/ui/button.d.ts +10 -0
  29. package/dist/components/ui/button.js +31 -0
  30. package/dist/components/ui/card.d.ts +9 -0
  31. package/dist/components/ui/card.js +24 -0
  32. package/dist/data/betaMarketingData.d.ts +15 -0
  33. package/dist/data/betaMarketingData.js +32 -0
  34. package/dist/index.d.ts +18 -0
  35. package/dist/index.js +20 -0
  36. package/dist/lib/utils.d.ts +2 -0
  37. package/dist/lib/utils.js +5 -0
  38. package/dist/types/connections-api.d.ts +35 -0
  39. package/dist/types/connections-api.js +1 -0
  40. package/dist/types/connections.d.ts +24 -0
  41. package/dist/types/connections.js +1 -0
  42. package/package.json +21 -16
package/README.md CHANGED
@@ -1,20 +1,92 @@
1
1
  # Fask UI Commons
2
2
 
3
- A minimal package containing common UI components and utilities shared between Fask applications:
3
+ A collection of reusable React components and data exports for Fask applications.
4
4
 
5
- * @/fask-web-app-v2 (B2C) is hosted at fask.ai
6
- * @/fask-biz-landing (B2B landing) is at fask.ai/business
7
- * @/powpal-web-app (B2B dashboard) is at biz.fask.ai
5
+ ## Data Exports
8
6
 
9
- ## Installation
7
+ ### Beta Marketing Content
10
8
 
11
- ```bash
12
- yarn add @faskai/ui-commons@0.0.0-alpha.01
9
+ Centralized marketing content for Beta features and offers that can be used across different projects with client-specific styling.
10
+
11
+ #### betaMarketingFeatures
12
+
13
+ Array of feature objects with emoji, title, and description:
14
+
15
+ ```tsx
16
+ import { betaMarketingFeatures } from '@faskai/ui-commons';
17
+
18
+ // Each feature contains:
19
+ // { emoji: string, title: string, description: string }
20
+ ```
21
+
22
+ **Features included:**
23
+ - ⚡ **Fastest Voice** - Industry-leading response times
24
+ - 🧠 **Self-Learning** - Fine-tune with your data
25
+ - 👆 **Easiest Setup** - 1-click deployment worldwide
26
+ - 🌍 **Any Language** - Human-sounding in 100+ languages
27
+ - 🎥 **Truly Multi-Modal** - Voice, Video, and Text channels
28
+ - 📈 **Massively Scalable** - Unlimited concurrent calls and agents
29
+
30
+ #### betaOfferData
31
+
32
+ Beta launch offer content with structured data:
33
+
34
+ ```tsx
35
+ import { betaOfferData } from '@faskai/ui-commons';
36
+
37
+ // Contains:
38
+ // - badge: "LIMITED TIME"
39
+ // - title: "🚀 Beta Launch Special"
40
+ // - description: string
41
+ // - callouts: Array<{emoji, title, description}>
42
+ // - cta: {text, url}
43
+ // - disclaimer: string
13
44
  ```
14
45
 
15
- ## Usage
46
+ **Callouts included:**
47
+ - 💰 **50% OFF First Year** - Exclusive Beta pricing
48
+ - 🤝 **Priority Support** - Direct access to founders
16
49
 
17
- ### GTM Provider
50
+ #### Usage Example
51
+
52
+ ```tsx
53
+ import { betaMarketingFeatures, betaOfferData } from '@faskai/ui-commons';
54
+
55
+ function BetaFeatures() {
56
+ return (
57
+ <section>
58
+ {/* Features */}
59
+ {betaMarketingFeatures.map((feature) => (
60
+ <div key={feature.title}>
61
+ <span>{feature.emoji}</span>
62
+ <h3>{feature.title}</h3>
63
+ <p>{feature.description}</p>
64
+ </div>
65
+ ))}
66
+
67
+ {/* Beta Offer */}
68
+ <div>
69
+ <h2>{betaOfferData.title}</h2>
70
+ <p>{betaOfferData.description}</p>
71
+ {betaOfferData.callouts.map((callout) => (
72
+ <div key={callout.title}>
73
+ <span>{callout.emoji}</span>
74
+ <h4>{callout.title}</h4>
75
+ <p>{callout.description}</p>
76
+ </div>
77
+ ))}
78
+ <a href={betaOfferData.cta.url}>{betaOfferData.cta.text}</a>
79
+ </div>
80
+ </section>
81
+ );
82
+ }
83
+ ```
84
+
85
+ ## Components
86
+
87
+ ### GTMProvider
88
+
89
+ Google Tag Manager integration for tracking and analytics.
18
90
 
19
91
  ```tsx
20
92
  import { GTMProvider } from '@faskai/ui-commons';
@@ -28,7 +100,24 @@ function App() {
28
100
  }
29
101
  ```
30
102
 
31
- ### Tracking Events
103
+ ## Installation
104
+
105
+ ```bash
106
+ npm install @faskai/ui-commons
107
+ # or
108
+ yarn add @faskai/ui-commons
109
+ ```
110
+
111
+ ## Dependencies
112
+
113
+ - React 18+
114
+ - No styling dependencies (bring your own CSS framework)
115
+
116
+ ## License
117
+
118
+ Private - Fask AI
119
+
120
+ ## Tracking Events
32
121
 
33
122
  ```tsx
34
123
  import { trackEvent, trackConversion, trackLinkedInConversion } from '@faskai/ui-commons';
@@ -64,16 +153,27 @@ yarn build
64
153
  yarn publish
65
154
  ```
66
155
 
67
- ## Adding New Components
156
+ ## Design Philosophy
157
+
158
+ This package follows a **data-driven approach**:
159
+
160
+ - **Content centralization**: Marketing copy and data managed in one place
161
+ - **Styling flexibility**: Each project implements its own design system
162
+ - **Type safety**: Full TypeScript support for all exports
163
+ - **Zero styling opinions**: No CSS dependencies or styling conflicts
164
+
165
+ ## Adding New Data
68
166
 
69
- 1. Create your component in `src/components/`
167
+ 1. Create your data export in `src/data/`
70
168
  2. Export it from `src/index.ts`
71
169
  3. Build the package with `yarn build`
72
- 4. The component will be available in all consuming projects
170
+ 4. Import and use in your client projects with custom styling
73
171
 
74
- ## Current Components
172
+ ## Current Exports
75
173
 
76
174
  - **GTMProvider**: Google Tag Manager integration
175
+ - **betaMarketingFeatures**: Beta feature content array
176
+ - **betaOfferData**: Beta offer structured data
77
177
  - **trackEvent**: Custom event tracking
78
178
  - **trackConversion**: Google Ads conversion tracking
79
179
  - **trackLinkedInConversion**: LinkedIn Ads conversion tracking
@@ -0,0 +1,8 @@
1
+ interface AppIconProps {
2
+ src?: string;
3
+ icon?: string;
4
+ alt?: string;
5
+ className?: string;
6
+ }
7
+ export declare function AppIcon({ src, icon, alt, className }: AppIconProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Blocks, Landmark, Building2, CreditCard, ShoppingCart, Calendar, Mail, MessageSquare, } from 'lucide-react';
4
+ import { cn } from '../../lib/utils';
5
+ const iconMap = {
6
+ landmark: Landmark,
7
+ building2: Building2,
8
+ 'credit-card': CreditCard,
9
+ 'shopping-cart': ShoppingCart,
10
+ calendar: Calendar,
11
+ mail: Mail,
12
+ 'message-square': MessageSquare,
13
+ };
14
+ const FallbackIcon = Blocks;
15
+ export function AppIcon({ src, icon, alt, className }) {
16
+ if (src) {
17
+ return _jsx("img", { src: src, alt: alt, className: cn('rounded', className) });
18
+ }
19
+ const Icon = (icon && iconMap[icon]) || FallbackIcon;
20
+ return _jsx(Icon, { className: cn('text-muted-foreground', className) });
21
+ }
@@ -0,0 +1,10 @@
1
+ import type { FaskSearchableApp } from '../../types/connections';
2
+ interface AppSearchGridProps {
3
+ connectedAppKeys: string[];
4
+ onSearch: (query: string) => Promise<FaskSearchableApp[]>;
5
+ onConnect: (app: FaskSearchableApp) => void;
6
+ connecting?: string | null;
7
+ initialApps?: FaskSearchableApp[];
8
+ }
9
+ export declare function AppSearchGrid({ connectedAppKeys, onSearch, onConnect, connecting, initialApps, }: AppSearchGridProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { Loader2 } from 'lucide-react';
5
+ import { AppIcon } from './app-icon';
6
+ import { Badge } from '../ui/badge';
7
+ import { Button } from '../ui/button';
8
+ export function AppSearchGrid({ connectedAppKeys, onSearch, onConnect, connecting, initialApps, }) {
9
+ const [query, setQuery] = useState('');
10
+ const [apps, setApps] = useState(initialApps || []);
11
+ const [searching, setSearching] = useState(false);
12
+ const debounceRef = useRef(null);
13
+ useEffect(() => {
14
+ if (debounceRef.current)
15
+ clearTimeout(debounceRef.current);
16
+ debounceRef.current = setTimeout(async () => {
17
+ if (!query.trim()) {
18
+ setApps(initialApps || []);
19
+ return;
20
+ }
21
+ setSearching(true);
22
+ const results = await onSearch(query);
23
+ setApps(results);
24
+ setSearching(false);
25
+ }, 300);
26
+ return () => {
27
+ if (debounceRef.current)
28
+ clearTimeout(debounceRef.current);
29
+ };
30
+ }, [query, onSearch, initialApps]);
31
+ function isConnected(slug) {
32
+ return connectedAppKeys.includes(slug);
33
+ }
34
+ return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-medium", children: "Connect Apps" }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Search and connect apps for your agent." })] }), _jsx("input", { className: "border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border px-3 py-1 text-sm shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50", placeholder: "Search apps (e.g. Slack, Google Sheets, HubSpot)...", value: query, onChange: (e) => setQuery(e.target.value) }), searching && (_jsxs("div", { className: "flex items-center gap-2 py-4", children: [_jsx(Loader2, { className: "size-4 animate-spin" }), _jsx("span", { className: "text-muted-foreground text-sm", children: "Searching..." })] })), apps.length > 0 && (_jsx("div", { className: "grid gap-2 sm:grid-cols-2 lg:grid-cols-3", children: apps.map((app) => (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border p-3", children: [_jsx(AppIcon, { src: app.img_src, alt: app.name, className: "size-8" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "truncate text-sm font-medium", children: app.name }), isConnected(app.name_slug) && (_jsx("span", { className: "text-muted-foreground text-xs", children: "Connected" }))] }), isConnected(app.name_slug) ? (_jsx(Badge, { children: "Connected" })) : (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => onConnect(app), disabled: connecting === app.name_slug, children: [connecting === app.name_slug && (_jsx(Loader2, { className: "animate-spin" })), "Connect"] }))] }, app.name_slug))) }))] }));
35
+ }
@@ -0,0 +1,8 @@
1
+ import type { FaskConnection } from '../../types/connections';
2
+ interface AvailableAppsListProps {
3
+ connections: FaskConnection[];
4
+ onConnect: (connection: FaskConnection) => void;
5
+ connecting?: string | null;
6
+ }
7
+ export declare function AvailableAppsList({ connections, onConnect, connecting, }: AvailableAppsListProps): import("react/jsx-runtime").JSX.Element | null;
8
+ export {};
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Link, Loader2 } from 'lucide-react';
4
+ import { AppIcon } from './app-icon';
5
+ import { Card, CardHeader, CardFooter } from '../ui/card';
6
+ import { Button } from '../ui/button';
7
+ export function AvailableAppsList({ connections, onConnect, connecting, }) {
8
+ const available = connections.filter((c) => c.status !== 'connected');
9
+ if (available.length === 0)
10
+ return null;
11
+ return (_jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: available.map((conn) => {
12
+ const isConnecting = connecting === conn.appKey;
13
+ return (_jsxs(Card, { className: "gap-4 py-4", children: [_jsxs(CardHeader, { className: "flex-row items-center gap-3", children: [_jsx("div", { className: "bg-background flex size-12 shrink-0 items-center justify-center rounded-lg border p-2", children: _jsx(AppIcon, { src: conn.app?.img, icon: conn.app?.icon, alt: conn.app?.name, className: "size-full object-contain" }) }), _jsx("p", { className: "min-w-0 flex-1 truncate text-base font-semibold", children: conn.app?.name })] }), _jsx(CardFooter, { children: _jsxs(Button, { className: "w-full", onClick: () => onConnect(conn), disabled: isConnecting, children: [isConnecting ? (_jsx(Loader2, { className: "animate-spin" })) : (_jsx(Link, {})), isConnecting ? 'Connecting...' : 'Connect'] }) })] }, conn.id || conn.appKey));
14
+ }) }));
15
+ }
@@ -0,0 +1,11 @@
1
+ import type { FaskConnection } from '../../types/connections';
2
+ interface ConnectedAppsListProps {
3
+ connections: FaskConnection[];
4
+ variant: 'cards' | 'badges';
5
+ onReimport?: (connection: FaskConnection) => void;
6
+ onDisconnect?: (connection: FaskConnection) => void;
7
+ reimporting?: string | null;
8
+ disconnecting?: string | null;
9
+ }
10
+ export declare function ConnectedAppsList({ connections, variant, onReimport, onDisconnect, reimporting, disconnecting, }: ConnectedAppsListProps): import("react/jsx-runtime").JSX.Element | null;
11
+ export {};
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Check, Loader2, RefreshCw, Unlink } from 'lucide-react';
4
+ import { AppIcon } from './app-icon';
5
+ import { Card, CardHeader, CardFooter, CardAction } from '../ui/card';
6
+ import { Badge } from '../ui/badge';
7
+ import { Button } from '../ui/button';
8
+ export function ConnectedAppsList({ connections, variant, onReimport, onDisconnect, reimporting, disconnecting, }) {
9
+ const connected = connections.filter((c) => c.status === 'connected');
10
+ if (connected.length === 0)
11
+ return null;
12
+ if (variant === 'badges') {
13
+ return (_jsx("div", { className: "flex flex-wrap gap-2", children: connected.map((conn) => (_jsxs(Badge, { children: [_jsx(AppIcon, { src: conn.app?.img, icon: conn.app?.icon, alt: conn.app?.name, className: "size-4" }), conn.app?.name] }, conn.appKey))) }));
14
+ }
15
+ // cards variant
16
+ return (_jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: connected.map((conn) => {
17
+ const isReimporting = reimporting === (conn.providerConnectionId || conn.appKey);
18
+ const isDisconnecting = disconnecting === (conn.id || conn.appKey);
19
+ return (_jsxs(Card, { className: "gap-4 py-4", children: [_jsxs(CardHeader, { className: "flex-row items-center gap-3", children: [_jsx("div", { className: "bg-background flex size-12 shrink-0 items-center justify-center rounded-lg border p-2", children: _jsx(AppIcon, { src: conn.app?.img, icon: conn.app?.icon, alt: conn.app?.name, className: "size-full object-contain" }) }), _jsx("p", { className: "min-w-0 flex-1 truncate text-base font-semibold", children: conn.app?.name }), _jsx(CardAction, { children: _jsxs(Badge, { variant: "outline", children: [_jsx(Check, { className: "size-3" }), "Connected"] }) })] }), _jsxs(CardFooter, { className: "gap-2", children: [onReimport && (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => onReimport(conn), disabled: isReimporting, children: [isReimporting ? (_jsx(Loader2, { className: "animate-spin" })) : (_jsx(RefreshCw, {})), isReimporting ? 'Reimporting...' : 'Reimport'] })), onDisconnect && (_jsxs(Button, { variant: "outline", size: "sm", className: "text-destructive hover:text-destructive", onClick: () => onDisconnect(conn), disabled: isDisconnecting, children: [isDisconnecting ? (_jsx(Loader2, { className: "animate-spin" })) : (_jsx(Unlink, {})), isDisconnecting ? 'Disconnecting...' : 'Disconnect'] }))] })] }, conn.id || conn.appKey));
20
+ }) }));
21
+ }
@@ -0,0 +1,9 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { ConnectionsApi } from '../../types/connections-api';
3
+ export declare function useConnectionsApi(): ConnectionsApi;
4
+ interface ConnectionsApiProviderProps {
5
+ api: ConnectionsApi;
6
+ children: ReactNode;
7
+ }
8
+ export declare function ConnectionsApiProvider({ api, children, }: ConnectionsApiProviderProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useContext } from 'react';
4
+ const ConnectionsApiContext = createContext(null);
5
+ export function useConnectionsApi() {
6
+ const api = useContext(ConnectionsApiContext);
7
+ if (!api) {
8
+ throw new Error('useConnectionsApi must be used within a ConnectionsApiProvider');
9
+ }
10
+ return api;
11
+ }
12
+ export function ConnectionsApiProvider({ api, children, }) {
13
+ return (_jsx(ConnectionsApiContext.Provider, { value: api, children: children }));
14
+ }
@@ -0,0 +1,7 @@
1
+ import type { FaskConnection } from '../../types/connections';
2
+ interface ConnectionsPanelProps {
3
+ variant: 'full-page' | 'compact';
4
+ onConnectionsLoaded?: (connections: FaskConnection[]) => void;
5
+ }
6
+ export declare function ConnectionsPanel({ variant, onConnectionsLoaded, }: ConnectionsPanelProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
@@ -0,0 +1,97 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useState } from 'react';
4
+ import { createFrontendClient } from '@pipedream/sdk/browser';
5
+ import { useConnectionsApi } from './connections-api-provider';
6
+ import { launchNangoConnect } from './launch-nango-connect';
7
+ import { ConnectedAppsList } from './connected-apps-list';
8
+ import { AvailableAppsList } from './available-apps-list';
9
+ import { AppSearchGrid } from './app-search-grid';
10
+ import { Card } from '../ui/card';
11
+ export function ConnectionsPanel({ variant, onConnectionsLoaded, }) {
12
+ const api = useConnectionsApi();
13
+ const [connections, setConnections] = useState([]);
14
+ const [initialApps, setInitialApps] = useState([]);
15
+ const [loading, setLoading] = useState(true);
16
+ const [connecting, setConnecting] = useState(null);
17
+ const [disconnecting, setDisconnecting] = useState(null);
18
+ const loadData = useCallback(async () => {
19
+ const conns = await api.getConnections();
20
+ setConnections(conns);
21
+ onConnectionsLoaded?.(conns);
22
+ }, [api, onConnectionsLoaded]);
23
+ useEffect(() => {
24
+ async function init() {
25
+ setLoading(true);
26
+ const [, apps] = await Promise.all([
27
+ loadData(),
28
+ api.searchApps(''),
29
+ ]);
30
+ setInitialApps(apps);
31
+ setLoading(false);
32
+ }
33
+ init();
34
+ }, [loadData, api]);
35
+ const connectedAppKeys = connections
36
+ .filter((c) => c.status === 'connected')
37
+ .map((c) => c.appKey);
38
+ async function handleConnect(app) {
39
+ const appKey = 'appKey' in app ? app.appKey : app.name_slug;
40
+ setConnecting(appKey);
41
+ try {
42
+ const info = await api.connect(appKey);
43
+ switch (info.provider) {
44
+ case 'pipedream': {
45
+ const pd = createFrontendClient({
46
+ externalUserId: 'user',
47
+ tokenCallback: async () => ({
48
+ token: info.token,
49
+ expiresAt: new Date(info.expiresAt),
50
+ connectLinkUrl: '',
51
+ }),
52
+ });
53
+ await new Promise((resolve, reject) => {
54
+ pd.connectAccount({
55
+ app: appKey,
56
+ onSuccess: () => resolve(),
57
+ onError: (err) => reject(err),
58
+ });
59
+ });
60
+ break;
61
+ }
62
+ case 'nango': {
63
+ await launchNangoConnect(info.connectLink, appKey, api);
64
+ break;
65
+ }
66
+ default: {
67
+ // Exhaustiveness check — TypeScript will error if a new ConnectInfo
68
+ // variant is added without a matching branch here.
69
+ const _exhaustive = info;
70
+ throw new Error(`Unknown connect provider: ${JSON.stringify(_exhaustive)}`);
71
+ }
72
+ }
73
+ await loadData();
74
+ }
75
+ finally {
76
+ setConnecting(null);
77
+ }
78
+ }
79
+ async function handleDisconnect(conn) {
80
+ const key = conn.id || conn.appKey;
81
+ setDisconnecting(key);
82
+ try {
83
+ await api.disconnect(conn.id);
84
+ await loadData();
85
+ }
86
+ finally {
87
+ setDisconnecting(null);
88
+ }
89
+ }
90
+ if (loading) {
91
+ return (_jsx("div", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: [1, 2, 3].map((i) => (_jsx(Card, { className: "h-28 animate-pulse bg-muted/50" }, i))) }));
92
+ }
93
+ if (variant === 'compact') {
94
+ return (_jsxs("div", { className: "space-y-6", children: [_jsx(ConnectedAppsList, { connections: connections, variant: "badges", onDisconnect: handleDisconnect, disconnecting: disconnecting }), _jsx(AppSearchGrid, { connectedAppKeys: connectedAppKeys, onSearch: (q) => api.searchApps(q), onConnect: (app) => handleConnect(app), connecting: connecting, initialApps: initialApps })] }));
95
+ }
96
+ return (_jsxs("div", { className: "space-y-6", children: [_jsx(ConnectedAppsList, { connections: connections, variant: "cards", onDisconnect: handleDisconnect, disconnecting: disconnecting }), _jsx(AvailableAppsList, { connections: connections, onConnect: handleConnect, connecting: connecting }), _jsx(AppSearchGrid, { connectedAppKeys: connectedAppKeys, onSearch: (q) => api.searchApps(q), onConnect: (app) => handleConnect(app), connecting: connecting, initialApps: initialApps })] }));
97
+ }
@@ -0,0 +1,13 @@
1
+ import type { ConnectionsApi } from '../../types/connections-api';
2
+ /**
3
+ * Open a Nango Connect popup and resolve when the connection appears in the
4
+ * backend (Nango calls our server-side webhook independently — polling is
5
+ * just how the UI discovers that). Rejects if the popup is closed before a
6
+ * connection is established or if the overall flow exceeds the timeout.
7
+ *
8
+ * We intentionally do not rely on Nango's `postMessage` events: those are
9
+ * not delivered when popup blockers interfere or when the auth flow ends on
10
+ * a different origin. Polling the connections list is browser-agnostic and
11
+ * works regardless of whether the popup ever posts back.
12
+ */
13
+ export declare function launchNangoConnect(connectLink: string, appKey: string, api: ConnectionsApi): Promise<void>;
@@ -0,0 +1,63 @@
1
+ const POPUP_FEATURES = 'width=600,height=700,popup=yes';
2
+ const POLL_INTERVAL_MS = 1500;
3
+ const POLL_TIMEOUT_MS = 5 * 60 * 1000;
4
+ /**
5
+ * Open a Nango Connect popup and resolve when the connection appears in the
6
+ * backend (Nango calls our server-side webhook independently — polling is
7
+ * just how the UI discovers that). Rejects if the popup is closed before a
8
+ * connection is established or if the overall flow exceeds the timeout.
9
+ *
10
+ * We intentionally do not rely on Nango's `postMessage` events: those are
11
+ * not delivered when popup blockers interfere or when the auth flow ends on
12
+ * a different origin. Polling the connections list is browser-agnostic and
13
+ * works regardless of whether the popup ever posts back.
14
+ */
15
+ export async function launchNangoConnect(connectLink, appKey, api) {
16
+ const popup = window.open(connectLink, 'fask-connect', POPUP_FEATURES);
17
+ if (!popup) {
18
+ throw new Error('Connect popup was blocked by the browser. Allow popups for this site and try again.');
19
+ }
20
+ const startedAt = Date.now();
21
+ return new Promise((resolve, reject) => {
22
+ const finish = (action) => {
23
+ clearInterval(intervalId);
24
+ if (!popup.closed)
25
+ popup.close();
26
+ action();
27
+ };
28
+ const intervalId = window.setInterval(async () => {
29
+ if (Date.now() - startedAt > POLL_TIMEOUT_MS) {
30
+ finish(() => reject(new Error('Connect flow timed out after 5 minutes.')));
31
+ return;
32
+ }
33
+ try {
34
+ const conns = await api.getConnections();
35
+ const found = conns.find((c) => c.appKey === appKey && c.status === 'connected');
36
+ if (found) {
37
+ finish(() => resolve());
38
+ return;
39
+ }
40
+ }
41
+ catch {
42
+ // Transient fetch error — keep polling. A persistent failure will
43
+ // hit the timeout above.
44
+ }
45
+ if (popup.closed) {
46
+ // Popup closed without the connection appearing — treat as cancel.
47
+ // Run one more check first in case the connection landed between
48
+ // the last poll and the close.
49
+ try {
50
+ const conns = await api.getConnections();
51
+ if (conns.find((c) => c.appKey === appKey && c.status === 'connected')) {
52
+ finish(() => resolve());
53
+ return;
54
+ }
55
+ }
56
+ catch {
57
+ // Ignore — fall through to cancel.
58
+ }
59
+ finish(() => reject(new Error('Connect window was closed before authorization completed.')));
60
+ }
61
+ }, POLL_INTERVAL_MS);
62
+ });
63
+ }
@@ -0,0 +1,7 @@
1
+ import { ReactNode } from 'react';
2
+ interface GTMProviderProps {
3
+ gtmId: string;
4
+ children: ReactNode;
5
+ }
6
+ export declare function GTMProvider({ gtmId, children }: GTMProviderProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import Script from 'next/script';
3
+ export function GTMProvider({ gtmId, children }) {
4
+ return (_jsxs(_Fragment, { children: [_jsx(Script, { id: "gtm-head", strategy: "afterInteractive", children: `
5
+ (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
6
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
7
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
8
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
9
+ })(window,document,'script','dataLayer','${gtmId}');
10
+ ` }), _jsx("noscript", { children: _jsx("iframe", { src: `https://www.googletagmanager.com/ns.html?id=${gtmId}`, height: "0", width: "0", style: { display: 'none', visibility: 'hidden' } }) }), children] }));
11
+ }
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ declare const GRID_STYLES: {
3
+ readonly 2: "grid-cols-1 gap-4 sm:grid-cols-2";
4
+ readonly 3: "grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3";
5
+ readonly 4: "grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3 xl:grid-cols-4";
6
+ };
7
+ declare function Grid({ columns, className, ...props }: React.ComponentProps<'div'> & {
8
+ columns: keyof typeof GRID_STYLES;
9
+ }): import("react/jsx-runtime").JSX.Element;
10
+ export { Grid };
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from '../../lib/utils';
3
+ const GRID_STYLES = {
4
+ 2: 'grid-cols-1 gap-4 sm:grid-cols-2',
5
+ 3: 'grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3',
6
+ 4: 'grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3 xl:grid-cols-4',
7
+ };
8
+ function Grid({ columns, className, ...props }) {
9
+ return (_jsx("div", { "data-slot": "grid", className: cn('grid', GRID_STYLES[columns], className), ...props }));
10
+ }
11
+ export { Grid };
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ declare function Row({ gap, fill, wrap, className, ...props }: React.ComponentProps<'div'> & {
3
+ gap?: 0 | 1 | 1.5 | 2 | 3 | 4 | 6 | 8;
4
+ fill?: boolean;
5
+ wrap?: boolean;
6
+ }): import("react/jsx-runtime").JSX.Element;
7
+ export { Row };
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from '../../lib/utils';
3
+ const GAP_MAP = {
4
+ 0: 'gap-0',
5
+ 1: 'gap-1',
6
+ 1.5: 'gap-1.5',
7
+ 2: 'gap-2',
8
+ 3: 'gap-3',
9
+ 4: 'gap-4',
10
+ 6: 'gap-6',
11
+ 8: 'gap-8',
12
+ };
13
+ function Row({ gap = 2, fill = false, wrap = false, className, ...props }) {
14
+ return (_jsx("div", { "data-slot": "row", className: cn('flex flex-row min-w-0 overflow-hidden items-center', GAP_MAP[gap], fill && 'flex-1', wrap && 'flex-wrap', className), ...props }));
15
+ }
16
+ export { Row };
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+ declare function SplitView({ desktop, mobile, className, ...props }: Omit<React.ComponentProps<'div'>, 'children'> & {
3
+ desktop: React.ReactNode;
4
+ mobile: React.ReactNode;
5
+ }): import("react/jsx-runtime").JSX.Element;
6
+ export { SplitView };
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from '../../lib/utils';
3
+ function SplitView({ desktop, mobile, className, ...props }) {
4
+ return (_jsxs("div", { "data-slot": "split-view", className: cn('flex min-h-0 flex-1 flex-col', className), ...props, children: [_jsx("div", { className: "hidden min-h-0 min-w-0 flex-1 overflow-hidden lg:flex", children: desktop }), _jsx("div", { className: "flex min-h-0 flex-1 flex-col lg:hidden", children: mobile })] }));
5
+ }
6
+ export { SplitView };
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+ declare function Stack({ gap, fill, className, ...props }: React.ComponentProps<'div'> & {
3
+ gap?: 0 | 1 | 1.5 | 2 | 3 | 4 | 6 | 8;
4
+ fill?: boolean;
5
+ }): import("react/jsx-runtime").JSX.Element;
6
+ export { Stack };
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from '../../lib/utils';
3
+ const GAP_MAP = {
4
+ 0: 'gap-0',
5
+ 1: 'gap-1',
6
+ 1.5: 'gap-1.5',
7
+ 2: 'gap-2',
8
+ 3: 'gap-3',
9
+ 4: 'gap-4',
10
+ 6: 'gap-6',
11
+ 8: 'gap-8',
12
+ };
13
+ function Stack({ gap = 4, fill = false, className, ...props }) {
14
+ return (_jsx("div", { "data-slot": "stack", className: cn('flex flex-col min-h-0 overflow-hidden', GAP_MAP[gap], fill && 'flex-1', className), ...props }));
15
+ }
16
+ export { Stack };
@@ -0,0 +1,9 @@
1
+ import * as React from 'react';
2
+ import { type VariantProps } from 'class-variance-authority';
3
+ declare const badgeVariants: (props?: ({
4
+ variant?: "default" | "secondary" | "destructive" | "outline" | null | undefined;
5
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
+ declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & {
7
+ asChild?: boolean;
8
+ }): import("react/jsx-runtime").JSX.Element;
9
+ export { Badge, badgeVariants };
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { cva } from 'class-variance-authority';
4
+ import { cn } from '../../lib/utils';
5
+ const badgeVariants = cva('inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', {
6
+ variants: {
7
+ variant: {
8
+ default: 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
9
+ secondary: 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
10
+ destructive: 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
11
+ outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
12
+ },
13
+ },
14
+ defaultVariants: {
15
+ variant: 'default',
16
+ },
17
+ });
18
+ function Badge({ className, variant, asChild = false, ...props }) {
19
+ const Comp = asChild ? Slot : 'span';
20
+ return (_jsx(Comp, { "data-slot": "badge", className: cn(badgeVariants({ variant }), className), ...props }));
21
+ }
22
+ export { Badge, badgeVariants };
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import { type VariantProps } from 'class-variance-authority';
3
+ declare const buttonVariants: (props?: ({
4
+ variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
5
+ size?: "icon" | "default" | "sm" | "lg" | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<'button'> & VariantProps<typeof buttonVariants> & {
8
+ asChild?: boolean;
9
+ }): import("react/jsx-runtime").JSX.Element;
10
+ export { Button, buttonVariants };
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { cva } from 'class-variance-authority';
4
+ import { cn } from '../../lib/utils';
5
+ const buttonVariants = cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", {
6
+ variants: {
7
+ variant: {
8
+ default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
9
+ destructive: 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
10
+ outline: 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
11
+ secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
12
+ ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
13
+ link: 'text-primary underline-offset-4 hover:underline',
14
+ },
15
+ size: {
16
+ default: 'h-9 px-4 py-2 has-[>svg]:px-3',
17
+ sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
18
+ lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
19
+ icon: 'size-9',
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: 'default',
24
+ size: 'default',
25
+ },
26
+ });
27
+ function Button({ className, variant, size, asChild = false, ...props }) {
28
+ const Comp = asChild ? Slot : 'button';
29
+ return (_jsx(Comp, { "data-slot": "button", className: cn(buttonVariants({ variant, size, className })), ...props }));
30
+ }
31
+ export { Button, buttonVariants };
@@ -0,0 +1,9 @@
1
+ import * as React from 'react';
2
+ declare function Card({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
3
+ declare function CardHeader({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
4
+ declare function CardTitle({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
5
+ declare function CardDescription({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
6
+ declare function CardAction({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
7
+ declare function CardContent({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
8
+ declare function CardFooter({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
9
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, };
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from '../../lib/utils';
3
+ function Card({ className, ...props }) {
4
+ return (_jsx("div", { "data-slot": "card", className: cn('bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm', className), ...props }));
5
+ }
6
+ function CardHeader({ className, ...props }) {
7
+ return (_jsx("div", { "data-slot": "card-header", className: cn('@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6', className), ...props }));
8
+ }
9
+ function CardTitle({ className, ...props }) {
10
+ return (_jsx("div", { "data-slot": "card-title", className: cn('leading-none font-semibold', className), ...props }));
11
+ }
12
+ function CardDescription({ className, ...props }) {
13
+ return (_jsx("div", { "data-slot": "card-description", className: cn('text-muted-foreground text-sm', className), ...props }));
14
+ }
15
+ function CardAction({ className, ...props }) {
16
+ return (_jsx("div", { "data-slot": "card-action", className: cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className), ...props }));
17
+ }
18
+ function CardContent({ className, ...props }) {
19
+ return (_jsx("div", { "data-slot": "card-content", className: cn('px-6', className), ...props }));
20
+ }
21
+ function CardFooter({ className, ...props }) {
22
+ return (_jsx("div", { "data-slot": "card-footer", className: cn('flex items-center px-6 [.border-t]:pt-6', className), ...props }));
23
+ }
24
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, };
@@ -0,0 +1,15 @@
1
+ export declare const betaOfferData: {
2
+ badge: string;
3
+ title: string;
4
+ description: string;
5
+ callouts: {
6
+ emoji: string;
7
+ title: string;
8
+ description: string;
9
+ }[];
10
+ cta: {
11
+ text: string;
12
+ url: string;
13
+ };
14
+ disclaimer: string;
15
+ };
@@ -0,0 +1,32 @@
1
+ export const betaOfferData = {
2
+ badge: 'LIMITED TIME',
3
+ title: 'Beta Launch Special',
4
+ description: 'Join our exclusive Beta program and get direct access to our founders.',
5
+ callouts: [
6
+ {
7
+ emoji: '🌟',
8
+ title: 'Help Shape the Future',
9
+ description: 'Have significant influence in shaping our product roadmap and stay ahead of your competition.',
10
+ },
11
+ {
12
+ emoji: '🔧',
13
+ title: 'Deep Customizations',
14
+ description: 'Deeply customize AI agents on Fask to perform any task, enhance your CX and grow your business.',
15
+ },
16
+ {
17
+ emoji: '💰',
18
+ title: '50% Off First Year',
19
+ description: 'Exclusive Beta pricing for early adopters. Save hundreds on your first year subscription.',
20
+ },
21
+ {
22
+ emoji: '🤝',
23
+ title: 'Priority Support',
24
+ description: 'Get immediate, priority access to our core team for support, feature requests and guidance.',
25
+ },
26
+ ],
27
+ cta: {
28
+ text: 'Subscribe Now',
29
+ url: 'https://biz.fask.ai/billing?price-tier=beta&period=year',
30
+ },
31
+ disclaimer: 'Beta offer valid for limited customers only.',
32
+ };
@@ -0,0 +1,18 @@
1
+ export { GTMProvider } from './components/gtm/gtm-provider';
2
+ export { betaOfferData } from './data/betaMarketingData';
3
+ export { ConnectionsApiProvider, useConnectionsApi, } from './components/connections/connections-api-provider';
4
+ export { AppIcon } from './components/connections/app-icon';
5
+ export { ConnectedAppsList } from './components/connections/connected-apps-list';
6
+ export { AvailableAppsList } from './components/connections/available-apps-list';
7
+ export { AppSearchGrid } from './components/connections/app-search-grid';
8
+ export { ConnectionsPanel } from './components/connections/connections-panel';
9
+ export type { FaskConnection, FaskSearchableApp } from './types/connections';
10
+ export type { ConnectionsApi, ConnectInfo } from './types/connections-api';
11
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, } from './components/ui/card';
12
+ export { Badge, badgeVariants } from './components/ui/badge';
13
+ export { Button, buttonVariants } from './components/ui/button';
14
+ export { Stack } from './components/layout/stack';
15
+ export { Row } from './components/layout/row';
16
+ export { Grid } from './components/layout/grid';
17
+ export { SplitView } from './components/layout/split-view';
18
+ export { cn } from './lib/utils';
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ export { GTMProvider } from './components/gtm/gtm-provider';
2
+ export { betaOfferData } from './data/betaMarketingData';
3
+ // Connections
4
+ export { ConnectionsApiProvider, useConnectionsApi, } from './components/connections/connections-api-provider';
5
+ export { AppIcon } from './components/connections/app-icon';
6
+ export { ConnectedAppsList } from './components/connections/connected-apps-list';
7
+ export { AvailableAppsList } from './components/connections/available-apps-list';
8
+ export { AppSearchGrid } from './components/connections/app-search-grid';
9
+ export { ConnectionsPanel } from './components/connections/connections-panel';
10
+ // UI primitives (shadcn)
11
+ export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, } from './components/ui/card';
12
+ export { Badge, badgeVariants } from './components/ui/badge';
13
+ export { Button, buttonVariants } from './components/ui/button';
14
+ // Layout primitives
15
+ export { Stack } from './components/layout/stack';
16
+ export { Row } from './components/layout/row';
17
+ export { Grid } from './components/layout/grid';
18
+ export { SplitView } from './components/layout/split-view';
19
+ // Utilities
20
+ export { cn } from './lib/utils';
@@ -0,0 +1,2 @@
1
+ import { type ClassValue } from 'clsx';
2
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,5 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ export function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
@@ -0,0 +1,35 @@
1
+ import type { FaskConnection, FaskSearchableApp } from './connections';
2
+ /**
3
+ * Information returned from `ConnectionsApi.connect()` to drive the
4
+ * provider-specific Connect UX in `ConnectionsPanel`.
5
+ *
6
+ * Each variant carries exactly the data its launch flow needs:
7
+ * - `pipedream` → token + expiresAt, passed to Pipedream's frontend SDK
8
+ * (`createFrontendClient` + `connectAccount`) which renders Pipedream's
9
+ * hosted popup.
10
+ * - `nango` → connectLink, a Nango Connect URL that we open in a popup and
11
+ * poll the connections list to detect completion.
12
+ *
13
+ * The discriminator (`provider`) MUST be present so `ConnectionsPanel` can
14
+ * branch exhaustively. Adding a new provider means adding a new variant here
15
+ * and a new branch in the panel — TypeScript will catch missing branches.
16
+ */
17
+ export type ConnectInfo = {
18
+ provider: 'pipedream';
19
+ token: string;
20
+ expiresAt: string;
21
+ } | {
22
+ provider: 'nango';
23
+ connectLink: string;
24
+ };
25
+ export interface ConnectionsApi {
26
+ getConnections(): Promise<FaskConnection[]>;
27
+ searchApps(query: string): Promise<FaskSearchableApp[]>;
28
+ /**
29
+ * Start a connect flow for `appKey`. Returns provider-specific launch info
30
+ * that `ConnectionsPanel` uses to render the appropriate UI (Pipedream
31
+ * popup vs Nango Connect popup).
32
+ */
33
+ connect(appKey: string): Promise<ConnectInfo>;
34
+ disconnect(connectionId: string): Promise<void>;
35
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,24 @@
1
+ export interface FaskConnection {
2
+ id?: string;
3
+ appKey: string;
4
+ provider: string;
5
+ providerConnectionId?: string;
6
+ status: 'connected' | 'available' | 'disconnected' | 'pending' | 'error';
7
+ app: {
8
+ name: string;
9
+ img?: string;
10
+ icon?: string;
11
+ };
12
+ actions?: {
13
+ key: string;
14
+ appKey: string;
15
+ name: string;
16
+ description: string;
17
+ }[];
18
+ }
19
+ export interface FaskSearchableApp {
20
+ name_slug: string;
21
+ name: string;
22
+ img_src: string;
23
+ provider: string;
24
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@faskai/ui-commons",
3
3
  "author": "Fask AI <arko@fask.ai>",
4
- "version": "0.0.0-alpha.03",
5
- "description": "Common UI components and utilities for Fask applications",
4
+ "version": "0.0.0-alpha.30",
5
+ "description": "Common UI components for Fask applications.",
6
6
  "private": false,
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
@@ -11,23 +11,28 @@
11
11
  "dist"
12
12
  ],
13
13
  "scripts": {
14
- "build": "tsc",
15
- "dev": "tsc --watch",
16
- "clean": "rm -rf dist",
17
- "prepare": "yarn build"
14
+ "build": "tsc"
18
15
  },
19
16
  "peerDependencies": {
20
- "next": "^14.0.0",
21
- "react": "^18.0.0",
22
- "react-dom": "^18.0.0"
17
+ "@pipedream/sdk": ">=2.3.0",
18
+ "@radix-ui/react-slot": ">=1.0.0",
19
+ "class-variance-authority": ">=0.7.0",
20
+ "clsx": ">=2.0.0",
21
+ "lucide-react": ">=0.300.0",
22
+ "next": ">=13.0.0",
23
+ "react": ">=18.0.0",
24
+ "tailwind-merge": ">=2.0.0"
23
25
  },
24
26
  "devDependencies": {
27
+ "@pipedream/sdk": "^2.3.6",
28
+ "@radix-ui/react-slot": "^1.1.0",
25
29
  "@types/react": "^18.0.0",
26
- "@types/react-dom": "^18.0.0",
27
- "@types/node": "^20.0.0",
28
- "typescript": "^5.0.0",
29
- "react": "^18.0.0",
30
- "react-dom": "^18.0.0"
30
+ "class-variance-authority": "^0.7.0",
31
+ "clsx": "^2.1.0",
32
+ "lucide-react": "^0.400.0",
33
+ "next": "^14.0.0",
34
+ "tailwind-merge": "^2.2.0",
35
+ "typescript": "^5.0.0"
31
36
  },
32
- "packageManager": "yarn@1.22.22"
33
- }
37
+ "dependencies": {}
38
+ }